Doing Many Things

2017-05-05

I often say that making code do one thing is easy, but making it do lots of things at once is hard. You especially see this in UI code, although it happens on the back end too. The worst of all is front-end web development, where so many things are outside your control.

For example, a customer asks to add a real-time “suggestions” feature to a search box, so that as users type they see possible things to search for. That’s pretty easy, until you think about all the details you have to support:

  • It works in IE/Firefox/Chrome/Safari, across lots of versions.
  • It works on Windows/OS X/iOS/Linux.
  • It works on desktops/tablets/phones.
  • It works when the user resizes the browser window.
  • It works when a user has configured text to appear larger than normal.
  • It has a hover effect that changes the background color and the cursor.
  • It has a hover effect that bolds the suggestion text, but doesn’t resize the row’s height/width.
  • It works when there are two search boxes on the page.
  • It works in the header/body text/left sidebar.
  • It works inside a dropdown menu.
  • It works inside a div that is floated.
  • It works inside a div with absolute positioning.
  • It works inside a div with relative positioning.
  • It works inside flowed text.
  • It works in an iframe.
  • It works without resizing the containing element.
  • It works when the containing element has overflow: hidden.
  • It works on lots of different pages across the website.
  • It works on all our different websites.
  • It works when the doctype is HTML5, HTML 4 Loose, HTML 4 Strict, XHTML, and missing.
  • It works in Bootstrap/Foundation/our custom CSS. (Hopefully you can just pick one here!)
  • It works with vanilla Javascript/jQuery/Angular/React/Ember. (Ditto.)
  • It works with or without Turbolinks.
  • It works with Ajax.
  • It works when the Javascript/CSS is minified and concatenated.
  • It works when the HTML and other static assets are served from a CDN on a different domain name from the back end.
  • The suggestions cover whatever is below the search box.
  • … but new modal dialogs cover the suggestions.
  • It works inside a modal dialog.
  • The suggestions don’t extend outside the modal dialog, but you can scroll to see more of them.
  • It works when a modal dialog opens another modal dialog.
  • Clicking a suggestion takes you to the search results.
  • Clicking elsewhere hides the suggestions.
  • Clicking a link elsewhere on the page still works when suggestions are visible.
  • When in a modal dialog and showing suggestions, clicking in the dialog hides just the suggestions.
  • When in a modal dialog and showing suggestions, clicking outside the dialog hides the suggestions and the dialog too.
  • When the search box is close to the bottom of the page, suggestions appear above it instead of below.
  • Clicking a suggestion doesn’t show that funny dashed line.
  • Clicking a suggestion row in the margin around the text still chooses the suggestion.
  • It works when suggestions include quotes and angle brackets (", ', <, >).
  • It works in English/Spanish/French/Mandarin Chinese/etc.
  • It works with right-to-left text (e.g. Arabic).
  • When a suggestion is too long to fit, it appears with an ellipsis, but hovering shows a tooltip with the whole suggestion.
  • The ellipsis works correctly with Arabic and Chinese text.
  • The ellipsis doesn’t change when the text becomes bold from a hover.
  • The back button works as expected.
  • It works when there are no suggestions.
  • It works when there are 1,000 sugestions, but it doesn’t show them all.
  • You can use a scroll bar to see more suggestions.
  • Scrolling down far enough loads more suggestions from the back end.
  • A suggestion is guaranteed to have at least some search results.
  • Suggestions show how many search results they have before you click.
  • It is fast.
  • It has tests.

I’m not saying it’s reasonable to do all these things every time. You may have customers for whom supporting Arabic and Chinese is a waste of money. I didn’t even list the things users will ask for that are just crazy! I did throw in “search box inside a dropdown menu” to give you a mild taste, and I may have included others that are … questionable. But you have to at least think about them.

In any case, there is a lot to get right, and these details are the difference between polished and unpolished. I learned early in my career that if I deliver something where a few details are still not quite right, I will surely get caught and have to fix it, and the customer will be a lot happier if they never see the broken version in the first place. Polish takes time, but it is still required.

The problem is that completing the list gets harder and harder as you approach the end. No item by itself is hard, but adding the next item without breaking the others is hard. In relativity, accerlating at high velocity takes more energy than accelerating the same amount at low velocity, and the energy to reach c is infinite. Web development has a similar law: requirement n + 1 costs more than requirement n. Adding an ellpisis isn’t hard. Adding it in right-to-left text with an Arabic font on IE 6 in quirks mode when the user has large text enabled is hard.

And now, just to draw one more connection: one thing you learn from experience is ruthless simplification, and skepticism toward adding “concepts” to your app. If you have n features, and they all interact, the complexity grows not by O(n) but O(n^2). I’ve talked mostly about front-end work, but I think it is the same everywhere, and we just see it more on the front end because there it is so unavoidable. Some of it is multiple browsers, some of it is features users expect from any UI, but there our n is already high before we even do anything. In our own feature design we can at least be ruthless. On the front end, we just have to keep polishing. It makes me pity people who sell Wordpress plugins for a living.

As much as you can, it pays off tremendously to manage the combinatorial explosion. You can limit scope, you can stop supporting old browsers. You can design components to be isolated, since if they don’t interact they don’t raise each other’s n. Somehow you have to keep that number under control. The good news is that the marginal benefit of cutting n is large.

You can also write tests, which let you tolerate a higher n by making it less expensive to test in an n^2 world. Sadly browser tests are the most expensive to write, the most brittle (i.e. the shortest-lived), and the longest to run, but still I am always glad to have them.

I guess I have written myself into two morals here. One is: don’t permit any more complexity than you have to. The second is: sometimes you just have to keep polishing, also known as “That’s why they call it work!” Painfully, these really tug you in different directions. Choosing when to apply them is not easy.

Good luck!

blog comments powered by Disqus Next: Scaling Sidekiq