Oct 23, 2019
One of my favorite tech presentations is Rich Hickey’s Simple Made Easy. The premise of the talk is that simple and easy are not the same thing. In fact, you often sacrifice simplicity in pursuit of easiness. As Rich says, we consider something easy if it is “at hand.” Like the Staples Easy Button. Simplicity is something totally different. It is the absence of complexity - lots of moving parts entwined together in intricate ways. If an “easy button” really existed, it would be supported by a complex network of solutions that could take care of any problem.
Rich was talking about programming and how to keep code maintainable. Simple code is easier to understand and extend. But I apply this perspective to lots of things. For example, a bicycle is a simple machine. A quick glance reveals how it works and what every part does. But pedaling up a hill is not easy. A modern car is complex. There is a lot of stuff going on under the hood and nearly all drivers accept that they have no hope of understanding it all.
In building software, I have come to realize that users only value ease. A user wants the features he/she likes “at hand.” In a mature, multi-featured application, UI design is mainly focused on hiding some features to make the frequently used ones stand out. Users don’t want simple. Take away any feature and there will be complaints.
Developers want simple. They want to work with code that is understandable and behaves predictably. They realize that every new feature is supported by hundreds of lines of code that need to be tested with every modification. Much of Rich's talk deals with programming styles that unnecessarily create complexity. But some requirements will force even well designed code to become complex. Ironically, these requirements are often driven by a desire for a "simple and easy" user experience (personalization, natural language inputs, voice control...).
Why does this matter?
If we don't acknowledge that "simple and easy" are in conflict, there will be unmet expectations that lead to friction between stakeholders. Users can become impatient discussing complex details about their "simple feature." Development teams can feel under-appreciated for the effort required to do a "simple thing." The time taken to wrestle with ignored complexity can look like incompetence.
Take, for example, the Google search box. It is easy to use... just type in some text and click the button. But it is anything but simple. There is an art to constructing effective queries and a whole industry (SEO) dedicated to manipulating what comes back. There is also a set of features that makes the search box like the command line for the web. I can't tell you how many times I have heard "I just want something simple like Google." Google isn't simple. But it is easy. What makes Google search feel easy is that the core functionality is obvious and it gives useful feedback to help you get what you want. You may not get what you want on the first try, but it is easy to refine your search to hone in on your target. Voice assistants aim for the same level of ease but I find the trial/error loop to be more frustrating. That is probably because the system can return only one response and the feedback loop takes longer.
I know how annoying it can be for someone to pick at language and I am not advocating to constantly correct people on their word choices. But I do think it is important for everyone to understand what they are asking for and what they are giving up when they get it. We can do that by probing into what the user means by "simple." That question is reasonable because both "simple" and "easy" are subjective terms that require elaboration. When we document requirements, we should avoid all subjective language. After all, most of the work to achieve the perception of ease and simplicity is through iteration and refinement. These qualities are not intrinsic to the feature but rather to the sentiment of the user.
Apr 10, 2015
I tend to be more aggressive than most when it comes to ending support for outdated browsers. As I mentioned in this article about ending IE6 support 5 years ago, supporting old browsers is not worth additional cost and drag on innovation.
I am happy whenever I see a big web property end support for an old browser, but I was absolutely thrilled to see that starting on January 12th 2016, Microsoft will only support the most current version of Internet Explorer available for their supported operating systems. This means:
- IE8 and lower will be practically dead. It will only be supported on some embedded versions of Windows.
- IE9 will only be supported on Windows Vista (remember Vista?), which will lose support on April 11, 2017.
- IE10 will only be supported on older versions of Windows Server.
Even better, whenever Microsoft launches a new version of Internet Explorer, the older versions immediately lose support on mainstream desktops (and probably phones and tablets too).
Thanks Microsoft! It is a lot easier to stop supporting a browser if the software vendor behind it has also abandoned it.
Sep 22, 2014
I do a lot of code reviews. On average, I probably spend an hour a day looking at diffs. I find this an efficient use of time because I frequently spot bugs that wouldn't be revealed by regular testing. I can also prevent implementations that will create obstacles as we progress through the road map.
One of the anti-patterns that I have started to notice is what I call the "Too DRY Anti-Pattern." For the uninitiated, DRY stands for "Don't Repeat Yourself". It is a primary tenet in one of my favorite books: The Pragmatic Programmer: From Journeyman to Master
. Keeping your code DRY means using methods and functions rather than copying and pasting code and not storing the same information in multiple places. I have written about DRY in terms of content management in this blog before.
The Too DRY anti-pattern starts with the good intention of keeping the code DRY. The developer creates a function or method to avoid having the same code exist in lots of places. However, over time the developer starts to use that function in places that do not quite fit. The function gets more parameters and has increasingly complex internal logic to control its behavior in different cases. Too DRY functions are easy to spot. They have lots of intricate if-then logic that try to address a wide diversity of usage.
A counter-weight to the Too DRY anti-pattern is the Unix philosophy of doing "one thing and doing it well." When you run into a Too DRY function, use the Unix "one thing" philosophy to break it down. Perhaps create smaller functions that do specific elements of the larger Too Dry function and then have different functions that use those building blocks in different ways. Also, repeating code isn't always a bad thing if the code is small and performs a discrete function. For example, if you have some code that creates an object and sets its properties, it is OK to have similar instances of this logic if the properties need to be set to different values.
Like all things, best practices need to be applied thoughtfully rather than as rigid rules. So keep your code DRY, but not at the expense of simplicity.
Sep 11, 2014
A few months ago we announced the availability of the Lionbridge onDemand Public API. While there are many translation APIs in the marketplace, I think that ours is the most comprehensive. Most translation APIs offer one level of translation quality (for example, machine translation or crowd translation or professional translation). The onDemand API allows you to select a translation type that meets your needs. This means that you can integrate with one API and give your customers lots of options. We also support many different content types. For example, we can translate videos as well as basic documents. Lastly, we offer different payment options. The end customer can pay themselves or the money can flow through our integration partner. I think these differentiators are responsible for the rapid adoption we are seeing. We already have around ten third party integrations in production or nearing completion.
Personally, this has been a huge learning experience. While I have used many APIs and even helped developed a couple, this is the first time that I designed one from a blank page. The sheer number of approaches, options, and details that you face is enormous. And although the biases formed from personal experience are important, what really matters is the developer audience — not just in the decisions you make but also how you support them.
I hate the term best practice but here are some of the things we got right. I must admit that we didn't always land on all of these practices the first time but when we adopted them, we definitely saw results.
Build visibility through success. It is not easy to bring attention to your API. But one thing that we did not do is invest in marketing hype to get the word out. The person who will be choosing what API to use is a developer who is less easily swayed by traditional B2B marketing tactics. The best case is to make your integration partners successful and build visibility that way. I love to hear our early integrations talk about their translation feature. The better we can make the developer experience (through reliability, documentation, support, and service quality) the more quality relationships we will build. I would much rather see a positive mention in Stack Overflow (not there yet, but I can dream!) than a bunch of paid search ads.
- Learn from others. When first designing the API, I drew inspiration from other APIs that I liked to use. I also found great information from George Reese's The REST API Design Handbook.
- Get feedback. When first designing the API, I had the benefit of knowing who the first customers would be. I wrote the initial specification in a Google Doc and invited feedback. The first partners caught some important omissions and had some great ideas too. I also had different developers review the specification before development started.
- Be pragmatic about change. An API is a contract. It shouldn't change. However, in the early going there were a few cases where it made sense to change the specification. This was inconvenient and awkward for developers who are starting their integrations. However, it was much less painful than making changes later when the install base is bigger and changes would break working systems. Managing these changes requires a lot of humility (admitting you are not perfect) and effort to rebuild trust. Once the lead integration partner was getting close to being feature complete, however, we did lock down the version of the API and saved our changes for subsequent versions. Since the initial launch of the API to our beta partners, we have launched another version and are now working on a third. We handle versioning with an HTTP header so it is easy for a client application to update the version to get access to the new feature.
- Hire your first client. While any early beta customer will need to accept some level of instability, you don't want to risk a partnership with your earliest growing pains. For this reason, we hired a contract PHP programmer to develop and test code samples for working with the API. This external developer had no more access than a true partner developer would have. The experiences of this developer gave tremendous insight. It exposed ambiguity in the documentation where the API implementer interpreted different meaning than an external reader. It revealed cases where the test passed but externally developed code didn't work.
- Give client developers a plan of action. Like with most API's the hardest part of working with the onDemand API is authentication. For this reason, we recommend a development sequence that starts with the easiest API: List Services. We also wrote up a quick tutorial with a code sample to help. For our beta developers, we created a project plan template that developers could follow. It contained tasks for integrating with the various features and rough time estimates. I was surprised to learn how much the developers liked this. Personally, I hate being given a project plan that I had no input over.
- Provide fanatical support. Even though I love software development, I am the first to admit it can be frustrating. It is easy for developers to get bogged down and stuck. The smoothest implementations we have had were when the dev team actively engaged the client developers. I admit that sometimes I felt like my time was being stretched too thin. But when you think about it, how can you get more leverage than helping a partner use your service and bring you business? I also enjoyed working with external developers. First of all, they are savvier than regular business users so you can rule out silly things like being disconnected from the Internet. Second, it was fun to troubleshoot with them as they sent requests and we inspected what onDemand received. Pinpointing errors can save a client developer a lot of time. This work had the side benefit of improving our error handling to provide more informative responses.
- Treat your development sandbox like production. This should sound obvious but a lot of services that I have integrated with have very unstable developer sandboxes. They are not always available and they have issues that you don't see in production. There is nothing more frustrating than trying to fix your code and realizing that the problem is in an external system you are integrating with. For this reason, we manage the sandbox as a production environment. It gets the same monitoring as a production environment and we release new code to sandbox after it has been in production for a couple of days.
- Test like you mean it. Software is incredibly picky about the slightest detail so API testing needs to be extremely thorough. You can't just verify that you are getting data back. The data you get back needs to be precisely like what is in the documentation. For this reason we do what I call double-blind testing. The development team writes tests that run whenever new code is merged into the integration branch. An external QA team writes and runs tests based on the documentation whenever we have a release candidate. The hope is that having two independent approaches will everything. When it doesn't, we have a major debrief.
If you do it right, an API can revolutionize your business by opening channels that extend your reach. But getting it right requires commitment and attention to detail. If you underperform in any aspect (design, implementation, or support) your program is doomed. You won't always get everything right but if you maintain your focus and address your failures, you can get things back on track.
Aug 21, 2013
Last week, when we released a new version of Lionbridge onDemand, I was reminded of a valuable lesson: keep your deployments small. Up to that point, we had been upgrading the production environment on a regular basis — nearly daily. When a new feature was ready, we deployed it. But this development iteration had some systemic improvements (new service types, enterprise features…) so we held off to release everything at once. That was a mistake. The problem was that lots of configuration changes had been building up (new settings, new libraries on the server, etc.) and that led to a lot more work at launch time. As you would expect, some steps were missed that compromised functionality. It wasn't a huge deal. Nothing was obviously broken. There were just some "Oh SNAP!" moments along the way.
Thanks to that reminder, we are back on schedule with regular code deployments. Some of the updates will be "under the waterline" — invisible changes to the underlying architecture to support visitor facing features that we are still working on. The trick is to keep the back-log of un-deployed code to a minimum. In addition to reducing the risk of the actual deployment, smaller upgrades make it easier to troubleshoot issues because there are fewer potential causes. For further reading on this topic read Eric Reis's chapter on Continuous Deployment in Web Operations: Keeping the Data On Time
. David Hobbs also has some great articles on this topic in relation to website launches. The one that springs to mind is Site Launches: do as little as possible. Also, to work in this way, it is also critical that you use a mature code management strategy. A couple of months ago my team adopted the Git Flow branching strategy and it has been a huge help.
People getting used to this model of product management will find it counter-intuitive. They see upgrades as risky, scary things and the natural tendency is to avoid them. But the more you delay an upgrade, the scarier and riskier it gets. And that creates a vicious circle. Even if you know and agree with the small deployment approach, it is easy to justify delaying an upgrade because you want to do one more thing. That is what happened to me.
Jun 10, 2013
Estimation has long been a point of embarrassment for the software development community. A big part of that is unrealistic expectations set by association with construction and manufacturing industries. Software is unlike building houses and widgets because every software application is unique and unprecedented, whereas construction and manufacturing most often involve assembling well-understood components in repeatable ways. Truly innovative construction projects, like the Big Dig, and the Sydney Opera House, regularly overrun their estimates (by 3 times and 15 times respectively in these cases). They shouldn't be compared with building out a subdivision either.
A more appropriate analogy is a moving truck. If you have ever hired movers to help you move between homes, you know that the process starts by someone walking through your house and looking over your stuff. This person may pull out a tape measure a few times but she is not measuring everything and she is certainly not doing things like seeing if your ottoman can be neatly wedged underneath your dinner table. The real purpose of the exercise is to know how big a truck (and team) to bring on moving day. It is up to the movers to make the most of the space and there is a real art to that. Sometimes the moving team can see right away that the job is going to be a real squeeze and they think through every piece like a game of Tetris. But sometimes the truck is just too small, you need to call in another truck. Either that or pulverize your furniture into very compact dust.
The main point of this analogy is that precise software estimation is impossible at the detail level. It takes people who have done lots of projects to give a decent "truck-size" estimate, and even then they can be wrong. In fact, each task estimate will likely be wrong. Accuracy is achieved only at the aggregate level when the underestimates cancel out the overestimates. Just as important, an experienced team will be quick to identify the need to adjust: either by increasing the scope (size of the truck), by leaving low value items on the curb (decreasing scope), or by changing how things are packed together. The earlier you are aware of your constraints the more options you will have. Only inexperienced teams wait until the truck is full to realize that some adjustments need to be made.
Related: Work Breakdown Structure vs. Deadlines
May 09, 2013
Finding excellent technical people is a constant challenge that I struggle with every day. The traditional recruiting process of searching résumés for keywords has been broken for a long time. You need to use clever techniques like my "One Field Job Application" and engage in various communities to find people who are passionate about their craft and want to do great work.
Interviewing candidates is even harder than sourcing them. You don't want to waste anyone's time (including your own) but you also want to get a real feel of how someone works. Developers who work on large teams are particularly hard to evaluate because responsibilities are often ambiguous. It takes a while to learn that a candidate had a really mundane role on that very interesting project on the résumé or that he/she didn't have the professional curiosity to learn about what other people were doing.
Some recent experiences are leading me to believe that the ideal technical interview question is "Tell me about your setup." While the tools that people use may at first seem mundane and subjective, I am starting to believe that tools are a reflection of the attitude a person brings to his/her work. This theory was originally inspired by the site UsesThis, which contains personal descriptions of the tools that people use. The people that submit their setups are clearly passionate about their work and pay attention to the details. Most importantly, they identify with their work — and that is critical to the type of commitment that leads to greatness.
I am not so interested in what the tools are (although I am starting to see some interesting patterns that I might share in a later post). I am more interested in the why. If an interview candidate starts to light up when explaining why she loves to use something, you know right then that she loves her work and she loves being good at her work. This is especially true for people who like tools that require some dialing in but have incredible power. Users of these tools have proactively invested their time to get better at something. Contrast that with someone who is indifferent about his tools: who just uses what he is offered; who likes a tool because it has buttons that you can click that does things that he doesn't understand. To me, that is a sign that I am talking to a drone that is satisfied with the bare minimum. His passion lies elsewhere or doesn't exist at all.
Passionate professionals who care about their craft don't just make a team more effective, they also make work more fun because passion is contagious. When you see a colleague doing cool things, you want to learn from them. You are also inspired to raise the level of your own work. When your peers are just going through the motions, it has the opposite effect. You de-personalize and get cynical and start identifying with Dilbert.
Apr 24, 2013
I am working on this web application project that has a requirement for users to upload REALLY big files (as in 1 gigabyte plus). Given ubiquitous broadband (do people even call it broadband anymore?), and increased usage of video, I would not be surprised if lots of developers are seeing this requirement. The problem is that the basic HTML file upload field is horrible for very large files. It does not resume if there is a momentary interruption in transfer and the server gets stuck with one very large, resource-eating request. The traditional work-around is to use a flash application to upload the files. SWFUpload seems to be the most common implementation, but if you go to the home page, you will find that the project is no longer being maintained and is starting to break with new versions of Flash.
I was feeling pretty discouraged until I discovered PL Upload by Moxiecode. If Moxiecode sounds familiar, it is because they make TinyMCE, which has practically turned into the de facto standard rich text editor (WYSIWYG) for applications like content management systems. Sorry CK Editor.
I think the Moxiecode team is brilliant for seeing this problem and building such a nice solution. They seem to have used the same logic as when they came up with TinyMCE. Take a pervasive, nagging problem and build a clean solution that can become a de-facto standard. Their business model is to dual-license the software: GPLv2 for websites like mine and a commercial license for embedding the component in non-GPL software applications. I expect to see PL Upload surfacing in lots of content management systems just like I see TinyMCE.
Feb 15, 2013
I just ran across this great quote by Edsger W.Dijkstra:
It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration. (link)
The quote made me think of moving from Subverson to Git but there are lots of other great analogies. Going from document management to web content management can also be this way. For example, document management people tend to focus on files and metadata and it is hard for them to grasp that web content can be structured so there is no need to separate the two concepts. Breaking out of old models and thought habits is difficult, I find the only way to do it is to trust the new way and be willing to let go of your instincts. It is also helpful to have a zen-master style coach bringing attention to your implicit assumptions and supporting you when you stumble.
Dec 17, 2012
Warning: geeky coding post
One of my responsibilities at Lionbridge is running infrastructure for our many marketing sites and applications. Like all good technical organizations, our development and deployment processes revolve around a source code management (SCM) system (in our case Git). Because we are running on a CMS (Wordpress - hold the religious war on whether Wordpress is a CMS), our websites are a combination of platform (Wordpress), customization (plugins and themes), configuration, and content. The question is: how much of this do you put into your SCM? Let's start with the obvious. You don't put content in your SCM. With configuration, it depends. Some frameworks allow you to tier your configuration options between global and environment-specific. For example, this is how I handle Django settings. I haven't come up with a clever way to do this in Wordpress so, for now, I am keeping wp-config.php out of the SCM.
As for platform and customizations, my tendency has always been to keep the platform out of the SCM. In this model, your build process is to install the platform than then deploy the themes and plugins from source control. I have done many projects in this way and it worked quite well. But another developer set up our Wordpress infrastructure for these sites and his preference was to put the whole Wordpress install into SCM and do a "git pull" to update the whole site. I also didn't love the idea of using Git to deploy. I like to pull the code to some alternative machine (my workstation or a server), (compile/)package it, and then push it to the web farm. Fabric is an excellent tool for this. You can still use Fabric in this scenario to execute Git commands on the remote servers but it still doesn't feel right to me. I prefer that runtime servers have as little software running on them as possible.
At first I hated this set up. I missed a developer being able to install our themes and plugins into any old Wordpress instance. I had doubts about the complexity of administration settings and the potential of editing source-controlled files trough the web. But I kept an open mind and I have come around. Here are the reasons:
The upgrade process is a lot tighter. Now I just upgrade Wordpress on a development instance, test, and then deploy the upgrade through the normal code deployment process. Plugins are handled the same way.
Customization compatibility issues just go away. When we have an outside contractor work around the system by working in his/her own Wordpress we run into issues where the new code is incompatible with versions of Wordpress and our installed plugins. This simply doesn't happen when you check out the whole platform.
Your SCM provides diff'ing functionality. Suspect that one of your environments does not conform to the rest? Worried that you have been hacked? Simply do a "git status" and see what files are different from the repository.
One thing to keep in mind: in this model you don't do any upgrades or plugin installation through the web. You also need to turn off the ability to edit theme and plugin code through the web. We needed to do this anyway because we have a clustered infrastructure and those changes need to be propagated across every web server in the farm.
It took me a really long time to come around to liking this setup. I think a big part of that is my Java background. Java makes a big deal about packaging and deployment. Pulling directly from Git seems more common in the PHP world, which never had a build process. But now, I am pretty happy with this setup. The best part is ensuring against compatibility issues (#2 on my list above). I still prefer deploying code to servers rather than pulling code from them but my conviction is steadily eroding. It's kind of nice not needing scripts to move around files and avoid overwriting files that are managed independently on each environment.
Just out of curiosity, I would love to know what source code management and deployment processes other PHP developer use. Let me know.