1126 stories
·
1 follower

The Top 9 Animation Libraries for UI Designers in 2017

1 Share

Frontend design has been through a revolution in the last decade. In 2007, most of us were still designing static magazine layouts – in 2017 we're building 'digital machines' with thousands of resizing, coordinated, moving parts.

Quite simply, great UI designers need to be great animators with a rock-solid understanding of the underlying tech.

This is the latest update to our guide to helping you choose the right animation library for each task. We're going to run-through 9 free, well-coded animation libraries best-suited to UI design work – their strengths and weaknesses and when to choose each one.

Some are pure CSS. Others are JavaScript, but none require anything more than basic HTML/CSS understanding to be used.

Enjoy.

The 2017 Top 9 Animation Libraries List

  1. Animate.css
  2. Bounce.js
  3. AnimeJS
  4. Magic Animations
  5. DynCSS
  6. CSShake
  7. Hover.CSS
  8. Velocity.js
  9. AniJS

Animate.css

Animate.css is one of the smallest and most easy-to-use CSS animation libraries available. Applying the Animate library to your project is as simple as adding the required CSS classes to your HTML elements. You can also use jQuery to call the animations on a particular event.

Animate.css

  • Creators: Daniel Eden
  • Released: 2013
  • Current Version: 3.5.2
  • Most Recent Update: April 2017
  • Popularity: 41,000+ stars on GitHub
  • Description: "A cross-browser library of CSS animations. As easy to use as an easy thing."
  • Library Size: 43 kB
  • GitHub: https://github.com/daneden/animate.css
  • License: MIT

As of mid-2017, it still one of the most popular and widely-used CSS animation libraries and its minified file is small enough (16.6kb) for inclusion in mobile websites as well. It has 41,000 stars on Github and is used as a component in many larger projects.

Animate.css is still under active development after 4 years. We feel that this is one of the simplest and most robust animation libraries and would definitely recommend you to use this in your next project.

Bounce.js

Bounce.js is a tool and javascript library that focusses on providing a selection of unique bouncy CSS animations to your website.

Bounce.js

This project is open-source with its code on GitHub.

  • Creators: Tictail
  • Released: 2014
  • Current Version: 0.8.2
  • Most Recent Update: Feb 2015
  • Popularity: 4,967+ stars on GitHub
  • Description: "Create beautiful CSS3 powered animations in no time."
  • Library Size: 16 kB
  • GitHub: https://github.com/tictail/bounce.js
  • License: MIT

Bounce.js is a neat animation library shipped with about ten animation 'pre-sets' – hence the small size of the library. As with animate.css, the animations are smooth and flawless. You might want to consider using this library if your needs focus on 'pop and bubble' animation types and could benefit from a lower file size overhead.

AnimeJS

AnimeJS is described as a lightweight JavaScript animation library that 'works with any CSS Properties, individual CSS transforms, SVG or any DOM attributes, and JavaScript Objects'. It's pretty awesome – so awesome in fact, that the GIF capture I took below can't do justice to how smooth and buttery the motion is.

Bounce.js

This project is available on GitHub.

AnimeJS is only newcomer to our list but has won a lot of converts in the 12 months since it's creation. It's incredibly versatile and powerful and wouldn't be out of place being used within HTML games. The only real question is 'is it overkill for simple web apps'?

Maybe, but as its fast, small and relatively easy to learn, it's hard to find fault with it.

Magic Animations

Magic Animations has been one impressive animation libraries available. It has many different animations, many of which are quite unique to this library. As with Animate.css, you can implement Magic by simply importing the CSS file. You can also make use of the animations from jQuery. This project offers a particularly cool demo application.

Magic Animations

Continue reading %The Top 9 Animation Libraries for UI Designers in 2017%

Read the whole story
emrox
8 days ago
reply
Hamburg, Germany
Share this story
Delete

What you need to do a 64k intro

1 Share

Comments

Read the whole story
emrox
13 days ago
reply
Hamburg, Germany
Share this story
Delete

The Path to Design System Maturity

1 Share

Methodology

It’s a lot like growing up, but not as awkward and embarrassing.

Carbon, Lightning, Clarity, Polaris, Plasma, Build. Am I describing names of SpaceX rockets, or corporate design systems?

In today’s software landscape, we’re no longer limited to feelings of inadequacy from just perusing Dribbble. Now we can feel insecure because we’ve never created a design system even half as robust as the ones above.

But just like watching Lebron play basketball or Thom Yorke orchestrating a song, it’s important to remember that most of us simply aren’t equipped to pull that off. So rather than hanging just up our shoes and guitar (or whatever Radiohead plays on these days), we can look toward these best-in-class systems for inspiration, and t0 understand what the path to success looks like.

Nathan Curtis said that design systems should be treated like a product. In this light, you can see how systems have a well-defined growth path from fledgling startup to monolithic corporation on the NYSE. Design systems don’t have to be huge. And they rarely are.

Using product as a reference makes it easier to understand how to construct your own design system.

Avoid pedantic debates on what defines a design system. Instead, define the goals of your system, its target audience, and your MVP. This will help you make more effective decisions as to where you should direct your effort. More importantly, you won’t be paralyzed with where to begin.

Design Systems can be broken down into three overarching themes that map to common product maturity stages:

1. A System for Efficiency (Startup)

2. A System for Consistency (Scale-up)

3. A System for Optimization (Enterprise)

1. Efficiency (Startup)

It’s important to understand these levels of maturity reflect the stage of your design system, not your product team itself. Every team, new or mature, will find themselves starting a design system from scratch. In this stage, you are either part of a startup product yourself, or beginning to create your first design system in a mature organization.

Building Internal Design Efficiencies

Tools: Sketch, AtomicSubform

First and foremost, a design system must make designers themselves more efficient. This efficiency allows designers to churn out screens more quickly, and frees them up to stay ahead of the curve on innovative features. In my experience, no other goal in the rest of this article is worth pursuing without this one properly accounted for.

VMWare’s Clarity system provides a .sketch file as the foundation for their design team

The first step simply requires that your design team use a design tool that will even allow for efficiencies. Sketch, Atomic, and Subform are, in my opinion, the clear leaders. They have intelligence built into their tools while offering plenty of room for designers to explore. Sketch gets the nod from me because it has integrations for days which will make it easy to grow your system over time.

Create consistency amongst designers

Tools: Shared design systems like Brand.ai or Craft Library; Design system frameworks like Material Design or UX Power Tools.
UX Power Tools uses nested symbols to cascade changes from one source.

When you’re in the early stages of design system development, consistency often works upwards. Rather than a set of guidelines dictating practice, emerging patterns dictate guidelines (sometimes to a fault). Most of us designers would love to pause and establish design principles and usage guidelines, but those efforts will almost always be trumped by production. In the meantime, you should be able to lean on key principles and methodologies that govern good UX and product design.

2. Consistency (Scale-Up)

At this phase, your product is probably growing, and so is your team. You have enough experience to know what your product is starting to become, and that allows you to create more guidelines and principles to direct your work. During this phase, you’re also moving past your digital tools and beginning to build out communication tools. As your team grows across locations, your company acquires new products, or you hire more designers, the need for consistency increases tremendously.

WeWork’s explorations leading up to their Plasma system, from: https://medium.com/@andrewcouldwell/plasma-design-system-4d63fb6c1afc

Providing Rationale

Tools: Style Guide, Design Principles, Pattern Libraries

By now, you’re well-practiced in laying out screens and user flows, and have a good sense what you have to work with. Assuming your product has been designed with intention and grounded in core HCI principles, you can use your existing work as a starting point for distilling hidden principles that may have been guiding your work. Most importantly, it gives you time to pause and find ways to improve upon what you’ve done by establishing better guidelines going forward.

Incorporating Brand

Tools: Brand Guide, Voice and Tone Guidelines

Brand and product go hand-in-hand in today’s everything-as-a-service world. Up until this point, you may have been running lean or simply not had in-house resources to really truly own your product brand. Before you can truly close the consistency gap, you need to incorporate your brand into your product.

Naturally, Shopify has fantastic voice and tone guidelines.

This ensures that your design system is a reflection of how your product is presented to the market. It also helps bring other teams (marketing and branding) into the fold before making the final leap with development toward full optimization.

3. Optimization (Enterprise)

Here we are. The holy grail. This phase represents the maturity of systems that are the most often described today. There is a strong connection between design and development, and this is only made possible by the groundwork laid in the previous stages.

It is important to note that not every team needs to reach this level.

It all comes down to your own goals and product priorities. This stage requires maturity, buy-in, experience and a clear ROI across almost every department in your product team.

Development Consistency

Tools: HTML and Sass; Javascript or other middle-tier frameworks

Google has released dozens of tools, but one fairly simple example of a coded version of their framework is Material Design Lite. This is very reminiscent of Bootstrap, only based upon the core design principles of its own design system.

Material Design Lite provides code snippets that mirror the design framework.

Handoff Optimization

Tools: Custom systems and tools to keep design and dev assets in sync, Governing Strategies, Design Ops, Design Technologists

Recently, Airbnb blew up the designernet™ with their React Sketch.app:

Painting with Code

This is about as mature a system one could create because it also includes compiling tools and other things I am not smart enough to articulate. Airbnb projects a clear transparency and cooperation amongst their design and development teams.

Much like an enterprise company, when you reach this stage, processes and ownership become critical to oversee. While us outsiders can fawn over Airbnb’s system, the reality is that a system of that size and complexity will be a bear to manage and optimize internally for years to come. This is where DesignOps and governing strategies come into play.

It’s also where you can finally brand your design system with a name that sounds like a space shuttle, and light up the designernet yourself.

Summary

So what have we learned? Generally speaking, there’s no official definition for what a “design system” is other than a structured approach toward optimizing your product design workflow.

Like a software product, a design system will go through phases of maturity as it becomes more complex and serves the needs of individuals beyond the design team itself.

So before you swan dive into creating a design system, reflect on why you’re creating it, and establish a target MVP. Identify what problem(s) you’re trying to solve, and choose the most appropriate approach for your needs.

UX Power Tools makes sophisticated Sketch tools to make help you make your own design system. The best Sketch designers are using it, and I think you might like it, too. Check the demo!


The Path to Design System Maturity was originally published in UX Power Tools on Medium, where people are continuing the conversation by highlighting and responding to this story.

Read the whole story
emrox
15 days ago
reply
Hamburg, Germany
Share this story
Delete

Rails 5.1: Loving JavaScript, System Tests, Encrypted Secrets, and more

1 Share

In celebration of the 12th RailsConf in Phoenix, Arizona this week, we’re proud to announce that Rails 5.1 is ready in its final form! We’ve spent over 4,100 commits since Rails 5.0 making everything EASIER, SIMPLER, and, uhhh, FUNNER? (That’s a RailsConf joke).

The highlight reel hasn’t really changed since the first beta, but here’s a repeat:

Loving JavaScript

We’ve had a stormy, perhaps even contentious, relationship with JavaScript over the years. But that time is past. JavaScript has improved immensely over the past few years, particularly with the advent of ES6, and with package and compilation tools like Yarn and webpack. Rails is embracing both of these solutions with open arms and letting whatever past water flow under the bridge.

JavaScript and Ruby share a deep philosophical bond over language design, if not ecosystem management. Let’s focus on the aspects we have in common and help Rails programmers extract the best from JavaScript with the help of some key guiding conventions.

The improvements in Rails 5.1 focus on three major parts:

  1. Manage JavaScript dependencies from NPM via Yarn. Think of Yarn like Bundler for JavaScript (it even has Yehuda Katz involved!). This makes it easy to depend on libraries like React or anything else from NPM. Everything you depend on via Yarn is then made available to be required in the asset pipeline, just like vendored dependencies would have been. Just use the binstub bin/yarn to add dependencies.

  2. Optionally compile JavaScript with webpack. While there are a million different module bundlers/compilers for JavaScript, webpack is quickly emerging as the preeminent choice. We’ve made it easy to use webpack with Rails through the new Webpacker gem that you can configure automatically on new projects with --webpack (or even --webpack=react, --webpack=angular, or --webpack=vue for a tailored configuration). This is fully compatible with the asset pipeline, which you can continue to use for images, fonts, sounds, whatever. You can even have some JavaScript on the asset pipeline and some done via webpack. It’s all managed via Yarn that’s on by default.

  3. Drop jQuery as a default dependency. We used to require jQuery in order to provide features like data-remote, data-confirm, and the other parts of Rails UJS. This dependency is no longer necessary as we’ve rewritten rails-ujs to use vanilla JavaScript. You’re of course still free to use jQuery, but you no longer have to.

Thanks to Liceth Ovalles for her work on Yarn integration, Dangyi Liu for his work on rails-ujs, and Guillermo Iguaran for chaperoning the whole thing!

System tests

In my 2014 keynote at RailsConf, I spoke at length about how an over focus on unit tests (and TDD) has lead us astray. While unit tests are part of a complete testing solution, they’re not the most important one. Integration tests that verify behavior all the way from controllers through models and views should play a much bigger part. Rails already has a great answer for these baked in.

But integration tests do not help you test the entire system, if that system relies on JavaScript. And most major web systems today rely at least to some extent on JavaScript. That’s where system tests driven by a real browser come in.

There’s long been an answer for system tests like this in Ruby called Capybara. It’s just been kind of a journey to configure properly for Rails. So now we’ve baked them straight into the framework! You get a lovely wrapping of Capybara that’s preconfigured for Chrome and enhanced to provide failure screenshots as part of Action Dispatch. You also don’t have to worry about extra database cleanup strategies anymore because the baked in transactional tests now rollback system test changes.

These tests are not without trade-offs. It’s of course still slower to run through a whole browser setup than just test a model with a stubbed out database. But it also tests so much more. You’d do well to familiarize yourself with system tests and have them as part of your testing answer.

Thanks to Eileen M. Uchitelle for her work extracting this from Basecamp!

Encrypted secrets

If you’re checking production passwords, API keys, and other secrets undisguised into your revision control system, you’re doing it wrong. That’s not safe and you should stop it! Now that’s an easy prescription, but without a coherent answer to what you should do instead, it’s also not that helpful.

People have long been loading up the ENV to store these secrets or used a variety of other solutions. There are all sorts of trade-offs and drawbacks to the ENV-model, not least of which that you still need to store those secrets for real somewhere else.

Inspired by Ara T. Howard’s sekrets gem, we’ve built encrypted secrets management into Rails 5.1. You can setup a new encrypted secrets file with bin/rails secrets:setup. That’ll generate a master key you’ll store outside of the repository, but allow you to commit the actual production secrets to your revision control. They’re then decrypted in production either through an injected key file or through RAILS_MASTER_KEY in the ENV.

Thank you to Kasper Timm Hansen for the work on this and Ara for the inspiration!

Parameterized mailers

Action Mailer is modeled on Action Controller. It shares underpinnings through Abstract Controller, but it’s long been disadvantaged from its controller cousin in the way it can share logic between actions.

In Action Controller, it’s common to use before_action and similar callbacks to extract logic that applies to multiple actions. This is doable because the params hash is available before the action is invoked. But in Action Mailer, we’ve been using regular method signatures with explicit arguments, so those arguments haven’t been available to filters that run before the actions.

With Parameterized Mailers, we now give you the option of calling mailers with parameters that, like in controllers, are available before the action is invoked. This combines with the default to/from/reply_to headers to dramatically DRY-up some mailer actions.

It’s completely backwards compatible and you can convert just the mailers that stand to gain the most from extraction first.

Direct & resolved routes

We have a lovely, simple API for declaring new resource routes. But if you’d like to add new programmatic routes that has logic determining the final destination based on the parameters, well, you’d have to row your own boat with helpers and other messy approaches.

With directed routes, you can now declare programmatic routes that have the full power of Ruby to do different things depending on the parameters passed.

With resolved routes, you can reprogram the polymorphic look-up for models based straight to compatible methods. So this allow you to turn link_to @comment into a final route like message_path(@comment.parent, anchor: "comment_#{@comment.id}").

Thank you to Andrew White for making all this work!

Unify form_tag/form_for with form_with

We’ve long had two parallel structures for creating forms. Those that were based off records through form_for, where we used convention over configuration to extract the details, and manually configured ones using form_tag. Now we’ve unified these two hierarchies with form_with. A single root tree that you can configure through an inferred record or manually. It’s much nicer and simpler.

Thanks to Kasper Timm Hansen for this one too!

Everything else

In addition to the highlight reel, we have hundreds of other fixes and improvements across all the frameworks. Please peruse the CHANGELOGs to acquaint yourself with all the goodies:

We have a great summary of the high-level changes in the release notes.

Your release manager for Rails 5.1 was Rafael França.

As per our maintenance policy, the release of Rails 5.1 means that bug fixes will only apply to 5.1.x, regular security issues to 5.1.x and 5.0.x, and severe security issues to 5.1.x, 5.0.x, and 4.2.x. This means 4.x and below will essentially be unsupported!

Thank you to everyone in the community for their diligent job testing the beta and release candidates of Rails 5.1! We made more than 600 commits following bug reports and concerns raised through this process. Thank you! Gracias! Merci! TAK!

Read the whole story
emrox
15 days ago
reply
Hamburg, Germany
Share this story
Delete

Elements of JavaScript Style

1 Share
Out of the Blue — Iñaki Bolumburu (CC BY-NC-ND 2.0)

In 1920, “The Elements of Style” by William Strunk Jr. was published, which set guidelines for English language style that have lasted the test of time. You can improve your code by applying similar standards to your code style.

The following are guidelines, not immutable laws. There may be valid reasons to deviate from them if doing so clarifies the meaning of the code, but be vigilant and self-aware. These guidelines have stood the test of time for good reason: They’re usually right. Deviate from them only for good reason — not simply on a whim or a personal style preference.

Almost every guideline from the elementary principles of composition applies to source code:

  • Make the paragraph the unit of composition: One paragraph to each topic.
  • Omit needless words.
  • Use active voice.
  • Avoid a succession of loose sentences.
  • Keep related words together.
  • Put statements in positive form.
  • Use parallel construction on parallel concepts.

We can apply nearly identical concepts to code style:

  1. Make the function the unit of composition. One job for each function.
  2. Omit needless code.
  3. Use active voice.
  4. Avoid a succession of loose statements.
  5. Keep related code together.
  6. Put statements and expressions in positive form.
  7. Use parallel code for parallel concepts.

1. Make the function the unit of composition. One job for each function.

The essence of software development is composition. We build software by composing modules, functions, and data structures together.
Understanding how to write and compose functions is a fundamental skill for software developers.

Modules are simply collections of one or more functions or data structures, and data structures are how we represent program state, but nothing interesting happens until we apply a function.

In JavaScript, there are three kinds of functions:

  • Communicating functions: Functions which perform I/O.
  • Procedural functions: A list of instructions, grouped together.
  • Mapping functions: Given some input, return some corresponding output.

While all useful programs need I/O, and many programs follow some procedural sequences, the majority of your functions should be mapping functions: Given some input, the function will return some corresponding output.

One thing for each function: If your function is for I/O, don’t mix that I/O with mapping (calculation). If your function is for mapping, don’t mix it with I/O. By definition, procedural functions violate this guideline. Procedural functions also violate another guideline: Avoid a succession of loose statements.

The ideal function is a simple, deterministic, pure function:

  • Given some input, always return the same output
  • No side-effects

See also, “What is a Pure Function?”

2. Omit needless code.

“Vigorous writing is concise. A sentence should contain no unnecessary words, a paragraph no unnecessary sentences, for the same reason that a drawing should have no unnecessary lines and a machine no unnecessary parts. This requires not that the writer make all sentences short, or avoid all detail and treat subjects only in outline, but that every word tell.” [Needless words omitted.]
~ William Strunk, Jr., The Elements of Style

Concise code is critical in software because more code creates more surface area for bugs to hide in. Less code= fewer places for bugs to hide = fewer bugs.

Concise code is more legible because it has a higher signal-to-noise ratio: The reader must sift through less syntax noise to reach the meaning. Less code = less syntax noise = stronger signal for meaning transmission.

To borrow a word from The Elements of Style: Concise code is more vigorous.

function secret (message) {
return function () {
return message;
}
};

Can be reduced to:

const secret = msg => () => msg;

This is much more readable to those familiar with concise arrow functions (introduced in 2015 with ES6). It omits unnecessary syntax: Braces, the function keyword, and the return statement.

The first includes unnecessary syntax. Braces, the function keyword, and the return statement serve no purpose to those familiar with concise arrow syntax. It exists only to make the code familiar to those who are not yet fluent with ES6.

ES6 has been the language standard since 2015. It’s time to get familiar.

Omit Needless Variables

Sometimes we’re tempted to assign names to things that don’t really need to be named. The problem is that the human brain has a limited number of resources available in working memory, and each variable must be stored as a discrete quanta, occupying one of the available working memory slots.

For this reason, experienced developers learn to eliminate variables that don’t need to exist.

For example, in most situations, you should omit variables created only to name return values. The name of your function should provide adequate information about what the function will return. Consider the following:

const getFullName = ({firstName, lastName}) => {
const fullName = firstName + ' ' + lastName;
return fullName;
};

vs…

const getFullName = ({firstName, lastName}) => (
firstName + ' ' + lastName
);

Another common way developers can reduce variables is to take advantage of function composition and point-free style.

Point-free style is a way of defining functions without referencing the arguments on which the functions operate. Common ways of achieving point-free style include currying and function composition.

Let’s look at an example using curry:

const add2 = a => b => a + b;
// Now we can define a point-free inc()
// that adds 1 to any number.
const inc = add2(1);
inc(3); // 4

Take a look at the definition of the inc() function. Notice that it doesn’t use the function keyword, or the => syntax. There’s no place to list parameters, because the function doesn’t make use of a parameter list internally. Instead, it returns a function that knows how to deal with arguments.

Let’s look at another example using function composition. Function composition is the process of applying a function to the result of another function application. Whether you realize it or not, you use function composition all the time. You use it whenever you chain methods like .map() or promise.then(), for example. In it’s most basic form, it looks like this: f(g(x)). In algebra this composition is usually written f ∘ g (often pronounced, “f after g” or “f composed with g”).

When you compose two functions together, you eliminate the need to create a variable to hold the intermediary value between the two function applications. Let’s see how function composition can clean up some code:

const g = n => n + 1;
const f = n => n * 2;
// With points:
const incThenDoublePoints = n => {
const incremented = g(n);
return f(incremented);
};
incThenDoublePoints(20); // 42
// compose2 - Take two functions and return their composition
const compose2 = (f, g) => x => f(g(x));
// Point-free:
const incThenDoublePointFree = compose2(f, g);
incThenDoublePointFree(20); // 42

Note that you can do the same thing with any functor. A functor is anything you can map over, e.g., arrays (Array.map()) or promises (promise.then()). Let’s write another version of compose2 using map chaining for function composition:

const compose2 = (f, g) => x => [x].map(g).map(f).pop();
const incThenDoublePointFree = compose2(f, g);
incThenDoublePointFree(20); // 42

You’re doing much the same thing every time you use promise chains.

Virtually every functional programming library has at least two versions of compose utilities: compose() which applies functions right-to-left, and pipe(), which applies functions left-to-right.

Lodash calls them compose() and flow(). When I use them from Lodash, I always import it like this:

import flow as pipe from 'lodash/fp/flow';
pipe(g, f)(20); // 42

However, this isn’t much more code, and it does the same thing:

const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);
pipe(g, f)(20); // 42

If this function composition stuff sounds alien to you, and you’re not sure how you’d use it, give this careful thought:

The essence of software development is composition. We build applications by composing smaller modules, functions, and data structures.

From that you can conclude that understanding the tools of function and object composition are as fundamental as a home builder understanding drills and nail guns.

When you use imperative code to piece together functions with intermediary variables, that’s like composing those pieces with duct tape and crazy glue.

Remember:

  • If you can say the same thing with less code, without changing or obfuscating the meaning, you should.
  • If you can say the same thing with fewer variables, without changing or obfuscating the meaning, you should.

3. Use Active Voice

“The active voice is usually more direct and vigorous than the passive.” ~ William Strunk, Jr., The Elements of Style

Name things as directly as possible.

  • myFunction.wasCalled() is better than myFunction.hasBeenCalled()
  • createUser() is better than User.create()
  • notify() is better than Notifier.doNotification()

Name predicates and booleans as if they are yes or no questions:

  • isActive(user) is better than getActiveStatus(user)
  • isFirstRun = false; is better than firstRun = false;

Name functions using verb forms:

  • increment() is better than plusOne()
  • unzip() is better than filesFromZip()
  • filter(fn, array) is better than matchingItemsFromArray(fn, array)

Event Handlers

Event handlers and lifecycle methods are an exception to the verb rule because they’re used as qualifiers; instead of expressing what to do, they express when to do it. They should be named so that they read, “<when to act>, <verb>”.

  • element.onClick(handleClick) is better than element.click(handleClick)
  • component.onDragStart(handleDragStart) is better than component.startDrag(handleDragStart)

In the second forms, it looks like we’re trying to trigger the event, rather than respond to it.

Lifecycle Methods

Consider the following alternatives for a component hypothetical lifecycle method which exists to call a handler function before a component updates:

  • componentWillBeUpdated(doSomething)
  • componentWillUpdate(doSomething)
  • beforeUpdate(doSomething)

In the first example, we use passive voice (will be updated instead of will update). It is a mouthful, and not any more clear than the other alternatives.

The second example is much better, but the whole point of this lifecycle method is to call a handler. componentWillUpdate(handler) reads as if it will update the handler, but that’s not what we mean. We mean, “before the component updates, call the handler”. beforeComponentUpdate() expresses the intention more clearly.

We can simplify even further. Since these are methods, the subject (the component) is built-in. Referring to it in the method name is redundant. Think about how it would read if you called these methods directly: component.componentWillUpdate(). That’s like saying, “Jimmy Jimmy will have steak for dinner.” You don’t need to hear the subject’s name twice.

  • component.beforeUpdate(doSomething) is better than component.beforeComponentUpdate(doSomething)

Functional mixins are functions that add properties and methods to an object. The functions are applied one after the other in an pipeline — like an assembly line. Each functional mixin takes the instance as an input, and tacks some stuff onto it before passing it on to the next function in the pipeline.

I like to name functional mixins using adjectives. Not that you can often use “ing” or “able” suffixes to find useful adjectives. Examples:

  • const duck = composeMixins(flying, quacking);
  • const box = composeMixins(iterable, mappable);

4. Avoid a Succession of Loose Statements

“…a series soon becomes monotonous and tedious.”
~ William Strunk, Jr., The Elements of Style

Developers frequently string together sequences of events in a procedure: a group of loosely related statements designed to run one after the other. An excess of procedures is a recipe for spaghetti code.

Such sequences are frequently repeated by many parallel forms, each of them subtly and sometimes unexpectedly divergent. For example, a user interface component shares the same core needs with virtually all other user interface components. Its concerns can be broken up into lifecycle stages and managed by separate functions.

Consider the following sequence:

const drawUserProfile = ({ userId }) => {
const userData = loadUserData(userId);
const dataToDisplay = calculateDisplayData(userData);
renderProfileData(dataToDisplay);
};

This function is really handling three different things: loading data, calculating view state from loaded data, and rendering.

In most modern front-end application architectures, each of these concerns is considered separately. By separating these concerns, we can easily mix and match different functions for each concern.

For example, we could replace the renderer completely, and it would not impact the other parts of the program, e.g., React’s wealth of custom renderers: ReactNative for native iOS & Android apps, AFrame for WebVR, ReactDOM/Server for server-side rendering, etc…

Another problem with this function is that you can’t simply calculate the data to be displayed and generate the markup without first loading the data. What if you’ve already loaded the data? You end up doing work that you didn’t need to do in subsequent calls.

Separating the concerns also makes them independently testable. I like to unit test my applications and display test results with each change as I’m writing the code. However, if we’re tying render code to data loading code, I can’t simply pass some fake data to the rendering code for testing purposes. I have to test the whole component end-to-end — a process which can be time consuming due to browser loading, asynchronous network I/O, etc…

I won’t get immediate feedback from my unit tests. Separating the functions allows you to test units in isolation from each other.

This example already has separate functions which we can feed to different lifecycle hooks in the application. Loading can be triggered when the component is mounted by the application. Calculating & rendering can happen in response to view state updates.

The result is software with responsibilities more clearly delineated: Each component can reuse the same structure and lifecycle hooks, and the software performs better; we don’t repeat work that doesn’t need to be repeated in subsequent cycles.

5. Keep related code together.

Many frameworks and boilerplates prescribe a method of program organization where files are grouped by technical type. This is fine if you’re building a small calculator or To Do app, but for larger projects, it’s usually better to group files together by feature.

For example, here are two alternative file hierarchies for a To Do app by type and feature:

Grouped by type:

.
├── components
│ ├── todos
│ └── user
├── reducers
│ ├── todos
│ └── user
└── tests
├── todos
└── user

Grouped by feature:

.
├── todos
│ ├── component
│ ├── reducer
│ └── test
└── user
├── component
├── reducer
└── test

When you group files together by feature, you avoid scrolling up and down in your file list to find all the files you need to edit to get a single feature working.

Colocate files related by feature.

6. Put statements and expressions in positive form.

“Make definite assertions. Avoid tame, colorless, hesitating, non-committal language. Use the word not as a means of denial or in antithesis, never as a means of evasion.”
~ William Strunk, Jr., The Elements of Style
  • isFlying is better than isNotFlying
  • late is better than notOnTime

If Statements

if (err) return reject(err);
// do something...

…is better than:

if (!err) {
// ... do something
} else {
return reject(err);
}

Ternaries

{
[Symbol.iterator]: iterator ? iterator : defaultIterator
}

…is better than:

{
[Symbol.iterator]: (!iterator) ? defaultIterator : iterator
}

Prefer Strong Negative Statements

Sometimes we only care about a variable if it’s missing, so using a positive name would force us to negate it with the ! operator. In those cases, prefer a strong negative form. The word “not” and the ! operator create weak expressions.

  • if (missingValue) is better than if (!hasValue)
  • if (anonymous) is better than if (!user)
  • if (isEmpty(thing)) is better than if (notDefined(thing))

Avoid null and undefined arguments in function calls

Don’t require function callers to pass undefined or null in place of an optional parameter. Prefer named options objects instead:

const createEvent = ({
title = 'Untitled',
description = '',
timeStamp = Date.now()
}) => // ...
// later...
const birthdayParty = createEvent({
title = 'Birthday Party',
timeStamp = birthDay
});

…is better than:

const createEvent(
title = 'Untitled',
description = '',
timeStamp = Date.now()
);
// later...
const birthdayParty = createEvent(
'Birthday Party',
undefined, // This was avoidable
birthDay
);

Use Parallel Code for Parallel Concepts

“…parallel construction requires that expressions of similar content and function should be outwardly similar. The likeness of form enables the reader to recognize more readily the likeness of content and function.”
~ William Strunk, Jr., The Elements of Style

There are few problems in applications that have not been solved before. We end up doing the same things over and over again. When that happens, it’s an opportunity for abstraction. Identify the parts that are the same, and build an abstraction that allows you to supply only the parts that are different. This is exactly what libraries and frameworks do for us.

UI components are a great example. Less than a decade ago, it was common to mingle UI updates using jQuery with application logic and network I/O. Then people began to realize that we could apply MVC to web apps on the client-side, and people began to separate models from UI update logic.

Eventually, web apps landed on a component model approach, which lets us declaratively model our components using things like JSX or HTML templates.

What we ended up with is a way of expressing UI update logic the same way for every component, rather than different imperative code for each one.

For those familiar with components, it’s pretty easy to see how each component works: There’s some declarative markup expressing the UI elements, event handlers for hooking up behaviors, and lifecycle hooks for attaching callbacks that will run when we need them to.

When we repeat similar pattern for similar problems, anybody familiar with the pattern should be able to quickly learn what the code does.

Conclusion: Code Should Be Simple, Not Simplistic

Vigorous writing is concise. A sentence should contain no unnecessary words, a paragraph no unnecessary sentences, for the same reason that a drawing should have no unnecessary lines and a machine no unnecessary parts. This requires not that the writer make all sentences short, or avoid all detail and treat subjects only in outline, but that every word tell. [Emphasis added.]
~ William Strunk, Jr., The Elements of Style

ES6 was standardized in 2015, yet in 2017, many developers avoid features such as concise arrow functions, implicit return, rest and spread operators, etc… in the guise of writing code that is easier to read because it is more familiar. That is a big mistake. Familiarity comes with practice, and with familiarity, the concise features in ES6 are clearly superior to the ES5 alternatives: concise code is simple compared to the syntax-heavy alternative.

Code should be simple, not simplistic.

Given that concise code is:

  • Less bug prone
  • Easier to debug

And given that bugs:

  • Are extremely expensive to fix
  • Tend to cause more bugs
  • Interrupt the flow of normal feature development

And given that concise code is also:

  • Easier to write
  • Easier to read
  • Easier to maintain

It is worth the training investment to bring developers up to speed using techniques such as concise syntax, currying & composition. When we fail to do so for the sake of familiarity, we talk down to readers of our code so that they can better understand it, like an adult speaking baby-talk to a toddler.

Assume the reader knows nothing about the implementation, but do not assume that the reader is stupid, or that the reader doesn’t know the language.

Be clear, but don’t dumb it down. To dumb things down is both wasteful and insulting. Make the investment in practice and familiarity in order to gain a better programming vocabulary, and a more vigorous style.

Code should be simple, not simplistic.

Next Steps

The best way to learn clear style is exposure to clear examples. Members of EricElliottJS.com have access to lots of video lessons, packed with lots examples and exercises.

If you’re not a member, you’re missing out!

Eric Elliott is the author of “Programming JavaScript Applications” (O’Reilly), and “Learn JavaScript with Eric Elliott”. He has contributed to software experiences for Adobe Systems, Zumba Fitness, The Wall Street Journal, ESPN, BBC, and top recording artists including Usher, Frank Ocean, Metallica, and many more.

He spends most of his time in the San Francisco Bay Area with the most beautiful woman in the world.


Elements of JavaScript Style was originally published in JavaScript Scene on Medium, where people are continuing the conversation by highlighting and responding to this story.

Read the whole story
emrox
20 days ago
reply
Hamburg, Germany
Share this story
Delete

Ask HN: Self Hosted vs. Gmail / Outlook?

1 Comment
Comments
Read the whole story
emrox
22 days ago
reply
some useful solutions linked
Hamburg, Germany
Share this story
Delete
Next Page of Stories