Wednesday, August 1, 2007

The Manifesto

Here I am, starting a new company. I have a great partner, but he's not writing code, which leaves me free to try to articulate some of the things I think are important in an engineering culture, and to enforce them ruthlessly ... on myself.

Here is the manifesto. Since we plan on building a site using Ruby on Rails, it has a bit of a focus on those technical underpinnings. I've tried to call out the key items in bullet points.

The big picture

Stay grounded in the core questions:

  • What is the service are we offering?
  • What specifically are we trying to enable?
  • Are we making it easy for the people we serve?
  • Are we making it fun for them?

Why have conventions?

Formal conventions set the expectations that we have for ourselves, and for each other. They make explicit the things that are most important, with the hope that the details will take care of themselves. They provide tools by which we can hold each other accountable, in what is hopefully a positive way, to the shared task of creating great products built on a solid foundation of design and code.

  • Define conventions for things that matter
  • Keep to the conventions
  • Hold each other to high standards
  • Take pride in the work
  • Keep raising the bar

Design

We do our initial designs on paper or using simple design tools. The goal is to turn around designs quickly, and to focus on basic usability questions before focusing on beauty, graphics or color schemes.

We write documentation that describes use cases, starting with the simple and moving to the complex. As much as possible, we do this before we start writing code. The goal is to evaluate the quality of the user experience by attempting to document it up front. We iterate on documentation and design until we feel that we are describing the user experience that we want to offer.

We write code when we think that we have a design and documentation that is mostly complete and which fulfills our goals for the user. We expect to iterate after the code is written. We try to evaluate and iterate quickly, rather than letting mediocre results become accepted fact.

  • Use paper or simple graphics tools
  • Focus on flow and usability
  • Keep the big picture questions in mind
  • Think about error handling or odd use cases
  • Write documentation
  • Expect to make changes after the code is written
  • Evaluate and iterate quickly
  • Don't let mediocre aspects become an accepted fact

Code

The goal is to create a service built on a foundation of an elegant code base that is small and clean. We build tools and libraries. We avoid repetition in the code. The difference between 500 and 1000 lines of code isn't that much, but the difference between 50,000 and 100,000 lines is huge. The path to a bad outcome involves a series of cumulative decisions involving small bodies of code. Paying attention to the small pieces pays off in the aggregate.

We subscribe to the "broken windows" approach to maintaining a clean code base. Once a body of code starts to become "broken", it is easier to add new elements that are broken. By keeping the code clean, it becomes harder to add hacks later.

We write documentation including method and class headers, and we use tools to generate code documentation. We do this from the start, because while it's not very important in a small code base, it is difficult to go back and do later. In addition creating documentation requires some introspection on the part of the engineer, which hopefully leads to better code.

We try to keep controllers and views simple. It is easy to dump code into both places, but it produces code that is harder to understand and maintain. The effort to keep these clean results in helpers, which may not be immediately useful, but the simple act of creating libraries of code at least pushes us toward introspection and reuse.

We think in terms of APIs. When writing our controllers we think in terms of the API that we are providing to outside developers -- is it consistent? is it elegant? is it easy to understand? When we write library and helper methods, we look to achieve the same goals.

  • Write class and method headers suitable for automatically generating code documentation
  • Keep methods short, pulling out code into libraries if needed
  • Keep Controllers and Views as simple as possible
  • Do not repeat code, create libraries or helpers
  • Strive for elegance
  • Always look for simpler ways to do things
  • When you learn something new, look back at old code and apply that knowledge
  • Think in terms of APIs, and imagine other developers needing to understand how to use your code

Testing

We design with testing in mind. Where possible, we design features and write code which can tested using automated testing tools.

We write automated tests, and we write them as part of developing new features. When a feature is installed, it is expected that there is a valid and complete set of automated tests that will test the feature. This is particularly true with unit and functional tests (for models and controllers). For integration testing, we write stories that describe user behavior, and build tests around those stories.

We use these tests to help ensure that new changes haven't broken existing code. Testing by hand is hugely expensive in terms of time and morale. Automated tests don't remove the requirement for testing by hand, but they can dramatically reduce the amount of time required, and dramatically increase confidence in the stability of the code base.

As we find bugs, we decide whether the bug is a simple coding error, or whether it is rooted in a deeper logic or design mistake. If it is not a simple error, then we write a test to detect the error before we fix it. The goal is to ensure that the bug is fixed, but also to ensure that the same sort of bug won't be reintroduced later.

We write test code which is readable and documented. There may eventually be as much test code written as there is code in the site itself, and we should treat the test code as if it were part of the product.

For tools, we use test-spec to help write readable tests. We use the autotest testing library to warn us of tests which are failing due to changes we've made. No code should be installed that does not pass the existing suite of tests.

We use rcov before we install new code to evaluate how completely our tests are covering our code base. We evaluate the results to decide what tests we should add. Our goal is 100% coverage. When we install, we want to know that every line of code at least executes correctly in a basic usage testing model.

  • Design with testing in mind
  • Run autotest when you begin development
  • Write tests as part of developing new features
  • Use test-spec to make tests readable
  • Use RCov to evaluate the effectiveness the tests
  • Do not install code until tests exist, and produce 100% coverage

HTML, CSS and Javascript

We attempt to keep our HTML clean by separating out behavior into javascript files, and binding that behavior to DOM objects via the behavior library. This makes it easier for us to read and maintain HTML code, and easier for designers to work in our templates.

Where possible, we use CSS names that describe what things are, not how they look. So we prefer class names like "emailaddress" over "grayanditalic". We used nested CSS rules to affect differences in appearance in different contexts.

The goal is that the same CSS name will apply to the same type of object, regardless of where that object is placed on the site. This creates simple and self-documentating HTML code that is readable and can be maintained by both engineers and design staff.

We use partials to avoid duplication of HTML. All the same issues which apply to source code apply to HTML, CSS and javascript. It should be clean, elegant, and should not include duplication.

  • Keep the HTML clean
  • Use CSS class names that describe what something is, not how it should look
  • Use the same CSS class name to describe the same thing, no matter where it is
  • Use nested CSS class definitions to alter the look of similar items across the site
  • Where possible, put javascript in javascript files, not in HTML
  • Use behavior to bind javascript to HTML
  • Make it clean, elegant, and avoid duplication

Javascript

We try to minimize the amount of site logic built around javascript. Javascript is hard to test with automated testing tools, which means that to test large bodies of javascript code requires human testing across a broad range of browsers, which is expensive, time consuming and prone to error.

We prefer to have a hit on the server to fetch a small amount of data instead of having a separate body of code that we need to debug in javascript.

This does not mean that we won't render content in javascript (as the result of an ajax request to the server for data), but we avoid writing complex logic in javascript.

  • Keep the javascript as simple as possible
  • Prefer a small hit on the server over complex javascript logic
  • Focus javascript around simple effects or rendering simple UI

1 comments:

Jim said...

Nicely said, John. These are all great goals. Best of luck on the new venture...