You are here

thinktime

Shields up

Seth Godin - Sun 12th Jun 2016 19:06
Do not tell your friends about your nascent idea, your notion, the area you hope to explore next. Do not seek reassurance from them. Do not become vulnerable about your tiny new sprout of an inkling. It will be extinguished...        Seth Godin
Categories: thinktime

"But where's the money?"

Seth Godin - Sat 11th Jun 2016 19:06
A colleague was talking to the CEO of a fast-growing small business about a partnership opportunity. The CEO said, "well, this is something we believe in, something we want to have happen," and then he continued, "in fact, it's something...        Seth Godin
Categories: thinktime

The marketing we deserve

Seth Godin - Fri 10th Jun 2016 19:06
We say we want sustainable packaging... but end up buying the one in fancy packaging instead. We say we want handmade, local goods... but end up buying the cheap one, because it's 'just as good.' We say we want the...        Seth Godin
Categories: thinktime

Your job vs. your project

Seth Godin - Thu 09th Jun 2016 18:06
Jobs are finite, specified and something we 'get'. Doing a job makes us defensive, it limits our thinking. The goal is to do just enough, not get in trouble, meet spec. When in doubt, seek deniability. Projects are open-ended, chosen...        Seth Godin
Categories: thinktime

"Um" and "like" and being heard

Seth Godin - Wed 08th Jun 2016 19:06
You can fix your "um" and you probably should. Each of us now owns a media channel and a brand, and sooner or later, as your work gains traction, we'll hear your voice. Either in a job interview or on...        Seth Godin
Categories: thinktime

Making your JavaScript Pure

a list apart - Wed 08th Jun 2016 00:06

Once your website or application goes past a small number of lines, it will inevitably contain bugs of some sort. This isn’t specific to JavaScript but is shared by nearly all languages—it’s very tricky, if not impossible, to thoroughly rule out the chance of any bugs in your application. However, that doesn’t mean we can’t take precautions by coding in a way that lessens our vulnerability to bugs.

Pure and impure functions

A pure function is defined as one that doesn’t depend on or modify variables outside of its scope. That’s a bit of a mouthful, so let’s dive into some code for a more practical example.

Take this function that calculates whether a user’s mouse is on the left-hand side of a page, and logs true if it is and false otherwise. In reality your function would probably be more complex and do more work, but this example does a great job of demonstrating:

function mouseOnLeftSide(mouseX) { return mouseX < window.innerWidth / 2; } document.onmousemove = function(e) { console.log(mouseOnLeftSide(e.pageX)); };

mouseOnLeftSide() takes an X coordinate and checks to see if it’s less than half the window width—which would place it on the left side. However, mouseOnLeftSide() is not a pure function. We know this because within the body of the function, it refers to a value that it wasn’t explicitly given:

return mouseX < window.innerWidth / 2;

The function is given mouseX, but not window.innerWidth. This means the function is reaching out to access data it wasn’t given, and hence it’s not pure.

The problem with impure functions

You might ask why this is an issue—this piece of code works just fine and does the job expected of it. Imagine that you get a bug report from a user that when the window is less than 500 pixels wide the function is incorrect. How do you test this? You’ve got two options:

  • You could manually test by loading up your browser and moving your mouse around until you’ve found the problem.
  • You could write some unit tests (Rebecca Murphey’s Writing Testable JavaScript is a great introduction) to not only track down the bug, but also ensure that it doesn’t happen again.

Keen to have a test in place to avoid this bug recurring, we pick the second option and get writing. Now we face a new problem, though: how do we set up our test correctly? We know we need to set up our test with the window width set to less than 500 pixels, but how? The function relies on window.innerWidth, and making sure that’s at a particular value is going to be a pain.

Benefits of pure functions Simpler testing

With that issue of how to test in mind, imagine we’d instead written the code like so:

function mouseOnLeftSide(mouseX, windowWidth) { return mouseX < windowWidth / 2; } document.onmousemove = function(e) { console.log(mouseOnLeftSide(e.pageX, window.innerWidth)); };

The key difference here is that mouseOnLeftSide() now takes two arguments: the mouse X position and the window width. This means that mouseOnLeftSide() is now a pure function; all the data it needs it is explicitly given as inputs and it never has to reach out to access any data.

In terms of functionality, it’s identical to our previous example, but we’ve dramatically improved its maintainability and testability. Now we don’t have to hack around to fake window.innerWidth for any tests, but instead just call mouseOnLeftSide() with the exact arguments we need:

mouseOnLeftSide(5, 499) // ensure it works with width < 500 Self-documenting

Besides being easier to test, pure functions have other characteristics that make them worth using whenever possible. By their very nature, pure functions are self-documenting. If you know that a function doesn’t reach out of its scope to get data, you know the only data it can possibly touch is passed in as arguments. Consider the following function definition:

function mouseOnLeftSide(mouseX, windowWidth)

You know that this function deals with two pieces of data, and if the arguments are well named it should be clear what they are. We all have to deal with the pain of revisiting code that’s lain untouched for six months, and being able to regain familiarity with it quickly is a key skill.

Avoiding globals in functions

The problem of global variables is well documented in JavaScript—the language makes it trivial to store data globally where all functions can access it. This is a common source of bugs, too, because anything could have changed the value of a global variable, and hence the function could now behave differently.

An additional property of pure functions is referential transparency. This is a rather complex term with a simple meaning: given the same inputs, the output is always the same. Going back to mouseOnLeftSide, let’s look at the first definition we had:

function mouseOnLeftSide(mouseX) { return mouseX < window.innerWidth / 2; }

This function is not referentially transparent. I could call it with the input 5 multiple times, resize the window between calls, and the result would be different every time. This is a slightly contrived example, but functions that return different values even when their inputs are the same are always harder to work with. Reasoning about them is harder because you can’t guarantee their behavior. For the same reason, testing is trickier, because you don’t have full control over the data the function needs.

On the other hand, our improved mouseOnLeftSide function is referentially transparent because all its data comes from inputs and it never reaches outside itself:

function mouseOnLeftSide(mouseX, windowWidth) { return mouseX < windowWidth / 2; }

You get referential transparency for free when following the rule of declaring all your data as inputs, and by doing this you eliminate an entire class of bugs around side effects and functions acting unexpectedly. If you have full control over the data, you can hunt down and replicate bugs much more quickly and reliably without chancing the lottery of global variables that could interfere.

Choosing which functions to make pure

It’s impossible to have pure functions consistently—there will always be a time when you need to reach out and fetch data, the most common example of which is reaching into the DOM to grab a specific element to interact with. It’s a fact of JavaScript that you’ll have to do this, and you shouldn’t feel bad about reaching outside of your function. Instead, carefully consider if there is a way to structure your code so that impure functions can be isolated. Prevent them from having broad effects throughout your codebase, and try to use pure functions whenever appropriate.

Let’s take a look at the code below, which grabs an element from the DOM and changes its background color to red:

function changeElementToRed() { var foo = document.getElementById('foo'); foo.style.backgroundColor = "red"; } changeElementToRed();

There are two problems with this piece of code, both solvable by transitioning to a pure function:

  1. This function is not reusable at all; it’s directly tied to a specific DOM element. If we wanted to reuse it to change a different element, we couldn’t.
  2. This function is hard to test because it’s not pure. To test it, we would have to create an element with a specific ID rather than any generic element.

Given the two points above, I would rewrite this function to:

function changeElementToRed(elem) { elem.style.backgroundColor = "red"; } function changeFooToRed() { var foo = document.getElementById('foo'); changeElementToRed(foo); } changeFooToRed();

We’ve now changed changeElementToRed() to not be tied to a specific DOM element and to be more generic. At the same time, we’ve made it pure, bringing us all the benefits discussed previously.

It’s important to note, though, that I’ve still got some impure code—changeFooToRed() is impure. You can never avoid this, but it’s about spotting opportunities where turning a function pure would increase its readability, reusability, and testability. By keeping the places where you’re impure to a minimum and creating as many pure, reusable functions as you can, you’ll save yourself a huge amount of pain in the future and write better code.

Conclusion

“Pure functions,” “side effects,” and “referential transparency” are terms usually associated with purely functional languages, but that doesn’t mean we can’t take the principles and apply them to our JavaScript, too. By being mindful of these principles and applying them wisely when your code could benefit from them you’ll gain more reliable, self-documenting codebases that are easier to work with and that break less often. I encourage you to keep this in mind next time you’re writing new code, or even revisiting some existing code. It will take some time to get used to these ideas, but soon you’ll find yourself applying them without even thinking about it. Your fellow developers and your future self will thank you.

Categories: thinktime

Try better

Seth Godin - Tue 07th Jun 2016 18:06
'Try harder' is something we hear a lot. After a while, though, we run out of energy for 'harder.' You can harangue people about trying harder all you like, but sooner or later, they come up empty. Perhaps it's worth...        Seth Godin
Categories: thinktime

On knowing it can be done

Seth Godin - Mon 06th Jun 2016 18:06
Can you imagine how difficult the crossword puzzle would be if any given answer might be, "there is no such word"? The reason puzzles work at all is that we know we should keep working on them until we figure...        Seth Godin
Categories: thinktime

This week's sponsor: FULLSTORY

a list apart - Mon 06th Jun 2016 14:06

FullStory, a pixel-perfect session playback tool that captures everything about your customer experience with one easy-to-install script.

Categories: thinktime

A ten-year plan is absurd

Seth Godin - Sun 05th Jun 2016 18:06
Impossible, not particularly worth wasting time on. On the other hand, a ten-year commitment is precisely what's required if you want to be sure to make an impact.        Seth Godin
Categories: thinktime

Neophilia and ennui

Seth Godin - Sat 04th Jun 2016 18:06
These are two sides of the same coin. Neophilia pushes us forward with wonder, eager for the next frontier. And ennui is the exhaustion we feel when we fall too in love with what might (should?) be next and ignore...        Seth Godin
Categories: thinktime

Add engines until airborne

Seth Godin - Fri 03rd Jun 2016 18:06
That's certainly one way to get through a thorny problem. The most direct way to get a jet to fly is to add bigger engines. And the easiest way to gain attention is to run more ads, or yell more...        Seth Godin
Categories: thinktime

All mirrors are broken

Seth Godin - Thu 02nd Jun 2016 19:06
It's impossible to see yourself as others do. Not merely because the medium is imperfect, but, when it comes to ourselves, we process what we see differently than everyone else in the world does. We make this mistake with physical...        Seth Godin
Categories: thinktime

Commit to Contribute

a list apart - Thu 02nd Jun 2016 00:06

One morning I found a little time to work on nodemon and saw a new pull request that fixed a small bug. The only problem with the pull request was that it didn’t have tests and didn’t follow the contributing guidelines, which results in the automated deploy not running.

The contributor was obviously extremely new to Git and GitHub and just the small change was well out of their comfort zone, so when I asked for the changes to adhere to the way the project works, it all kind of fell apart.

How do I change this? How do I make it easier and more welcoming for outside developers to contribute? How do I make sure contributors don’t feel like they’re being asked to do more than necessary?

This last point is important.

The real cost of a one-line change

Many times in my own code, I’ve made a single-line change that could be a matter of a few characters, and this alone fixes an issue. Except that’s never enough. (In fact, there’s usually a correlation between the maturity and/or age of the project and the amount of additional work to complete the change due to the growing complexity of systems over time.)

A recent issue in my Snyk work was fixed with this single line change:

In this particular example, I had solved the problem in my head very quickly and realized that this was the fix. Except that I had to then write the test to support the change, not only to prove that it works but to prevent regression in the future.

My projects (and Snyk’s) all use semantic release to automate releases by commit message. In this particular case, I had to bump the dependencies in the Snyk command line and then commit that with the right message format to ensure a release would inherit the fix.

All in all, the one-line fix turned into this: one line, one new test, tested across four versions of node, bump dependencies in a secondary project, ensure commit messages were right, and then wait for the secondary project’s tests to all pass before it was automatically published.

Put simply: it’s never just a one-line fix.

Helping those first pull requests

Doing a pull request (PR) into another project can be pretty daunting. I’ve got a fair amount of experience and even I’ve started and aborted pull requests because I found the chain of events leading up to a complete PR too complex.

So how can I change my projects and GitHub repositories to be more welcoming to new contributors and, most important, how can I make that first PR easy and safe?

Issue and pull request templates

GitHub recently announced support for issue and PR templates. These are a great start because now I can specifically ask for items to be checked off, or information to be filled out to help diagnose issues.

Here’s what the PR template looks like for Snyk’s command line interface (CLI) :

- [ ] Ready for review - [ ] Follows CONTRIBUTING rules - [ ] Reviewed by @remy (Snyk internal team) #### What does this PR do? #### Where should the reviewer start? #### How should this be manually tested? #### Any background context you want to provide? #### What are the relevant tickets? #### Screenshots #### Additional questions

This is partly based on QuickLeft’s PR template. These items are not hard prerequisites on the actual PR, but it does help in getting full information. I’m slowly adding these to all my repos.

In addition, having a CONTRIBUTING.md file in the root of the repo (or in .github) means new issues and PRs include the notice in the header:

Automated checks

For context: semantic release will read the commits in a push to master, and if there’s a feat: commit, it’ll do a minor version bump. If there’s a fix: it’ll do a patch version bump. If the text BREAKING CHANGE: appears in the body of a commit, it’ll do a major version bump.

I’ve been using semantic release in all of my projects. As long as the commit message format is right, there’s no work involved in creating a release, and no work in deciding what the version is going to be.

Something that none of my repos historically had was the ability to validate contributed commits for formatting. In reality, semantic release doesn’t mind if you don’t follow the commit format; they’re simply ignored and don’t drive releases (to npm).

I’ve since come across ghooks, which will run commands on Git hooks, in particular using a commit-msg hook validate-commit-msg. The installation is relatively straightforward, and the feedback to the user is really good because if the commit needs tweaking to follow the commit format, I can include examples and links.

Here’s what it looks like on the command line:

...and in the GitHub desktop app (for comparison):

This is work that I can load on myself to make contributing easier, which in turn makes my job easier when it comes to managing and merging contributions into the project. In addition, for my projects, I’m also adding a pre-push hook that runs all the tests before the push to GitHub is allowed. That way if new code has broken the tests, the author is aware.

To see the changes required to get the output above, see this commit in my current tinker project.

There are two further areas worth investigating. The first is the commitizenproject. Second, what I’d really like to see is a GitHub bot that could automatically comment on pull requests to say whether the commits are okay (and if not, direct the contributor on how to fix that problem) and also to show how the PR would affect the release (i.e., whether it would trigger a release, either as a bug patch or a minor version change).

Including example tests

I think this might be the crux of problem: the lack of example tests in any project. A test can be a minefield of challenges, such as these:

  • knowing the test framework
  • knowing the application code
  • knowing about testing methodology (unit tests, integration, something else)
  • replicating the test environment

Another project of mine, inliner, has a disproportionately high rate of PRs that include tests. I put that down to the ease with which users can add tests.

The contributing guide makes it clear that contributing doesn’t even require that you write test code. Authors just create a source HTML file and the expected output, and the test automatically includes the file and checks that the output is as expected.

Adding specific examples of how to write tests will, I believe, lower the barrier of entry. I might link to some sort of sample test in the contributing doc, or create some kind of harness (like inliner does) to make it easy to add input and expected output.

Fixing common mistakes

Something I’ve also come to accept is that developers don’t read contributing docs. It’s okay, we’re all busy, we don’t always have time to pore over documentation. Heck, contributing to open source isn’t easy.

I’m going to start including a short document on how to fix common problems in pull requests. Often it’s amending a commit message or rebasing the commits. This is easy for me to document, and will allow me to point new users to a walkthrough of how to fix their commits.

What’s next?

In truth, most of these items are straightforward and not much work to implement. Sure, I wouldn’t drop everything I’m doing and add them to all my projects at once, but certainly I’d include them in each active project as I work on it.

  1. Add issue and pull request templates.
  2. Add ghooks and validate-commit-msg with standard language (most if not all of my projects are node-based).
  3. Either make adding a test super easy, or at least include sample tests (for unit testing and potentially for integration testing).
  4. Add a contributing document that includes notes about commit format, tests, and anything that can make the contributing process smoother.

Finally, I (and we) always need to keep in mind that when someone has taken time out of their day to contribute code to our projects—whatever the state of the pull request—it’s a big deal.

It takes commitment to contribute. Let’s show some love for that.

Categories: thinktime

Read more blogs

Seth Godin - Wed 01st Jun 2016 18:06
Other than writing a daily blog (a practice that's free, and priceless), reading more blogs is one of the best ways to become smarter, more effective and more engaged in what's going on. The last great online bargain. Good blogs...        Seth Godin
Categories: thinktime

This week's sponsor: JIRA

a list apart - Wed 01st Jun 2016 07:06

Thanks to our sponsor Jira. Plan, track, and release world-class software with the #1 software development tool used by agile teams. Try JIRA for free today.

Categories: thinktime

Wasting our technology surplus

Seth Godin - Tue 31st May 2016 23:05
When someone handed you a calculator for the first time, it meant that long division was never going to be required of you ever again. A huge savings in time, a decrease in the cognitive load of decision making. Now...        Seth Godin
Categories: thinktime

The possibility of optimism (the optimism of possibility)

Seth Godin - Mon 30th May 2016 18:05
Is the glass half full or half empty? The pessimist sees what's present today and can only imagine eventual decline. The glass is already half empty and it's only going to get worse. The optimist understands that there's a difference...        Seth Godin
Categories: thinktime

Problems

Seth Godin - Sun 29th May 2016 19:05
Avoiding a problem with foresight and good design is a cheap, highly leveraged way to do your work. Extinguishing a problem before it gets expensive and difficult is almost as good, and far better than paying a premium when there's...        Seth Godin
Categories: thinktime

The originality paradox

Seth Godin - Sat 28th May 2016 18:05
There are a billion people trying to do something important for the first time. These people are connected by the net, posting, creating, daring to leap first. It's hard, because the number of people racing with you to be original...        Seth Godin
Categories: thinktime

Pages

Subscribe to kattekrab aggregator - thinktime