You work for me, Computer.

By Brandon Bloom

Language-oriented Programming: Too Much, Too Fast

There was some recent discussion on Hacker News about the 2004 article on “Language Oriented Programming” by Sergey Dmitriev. With my growing interest in programming languages, this article, and the pending release of JetBrain’s Meta Programming System, I have been thinking a lot about the future of programming.

As Sergey points out, MPS is not the first entry into this paradigm of software development. Intentional Programming was being demonstrated by Microsoft Research as early as 2000. Wikipedia lists several implementations of the language-oriented programming concept. (OK, that is enough links for now!) Sadly, none of these systems have been met with wide spread success. Despite my unlimited respect for the JetBrains team and love of their products — especially Resharper — I expect MPS to fail to achieve critical mass. I don’t think many programmers would disagree. It’s just too much, too fast.

Software development needs to evolve, not start anew. If you change too much at once, the common developer simply won’t accept it. Language designers are still re-inventing Lisp piece by piece in an effort to make sense of all the different concepts for Mort. In order to improve adoption of language-oriented programming concepts, the critical path must be identified and the programming community must be lead down that path one step at a time.

I believe that the first stepping stone is the box editor.

Both MPS and Intentional Programming provide a visual tree of boxes editor and store the abstract syntax tree in a sort of source code database. Both of these concepts are critical to power of language-oriented programming, but changing the storage of source code is simply too radical of a change. Well, actually, source code is routinely stored in databases for IDE features, but the authoritative storage always remains text files. All of a programmers most trusted tools operate at the string level and it is simply impractical to throw everything out at once.

This is why I propose the creation of a new editor which uses an abstract syntax tree box editing model, but preserves the source as text. This editor would have to play nice with other developers who are using traditional text editors. It should be possible for a single developer to try the editor for a day or two without another collaborator even noticing. There are clear benefits to a box editor even in the absence of language-oriented features. For example, navigation of source code by parent, child, or sibling relationships instead of by word or by line. Advanced renders can be used to layout math expressions or to show a referenced image file in a comment.

Editors are a religion, start a new one. If some box editor becomes popular enough, other tools would spring up around them. To help the process, the editor should have well exposed extension and composition mechanisms. For example, it should be trivially easy to add new renderers for various nodes. It should be equally trivial to utilize the abstract syntax tree system to create stand alone code analysis tools.

If box editors garner enough of a following, then the language-oriented programming advocates can seek the next stepping stone.

Imported Comments

Shawn Presser

“I believe that the first stepping stone is the box editor.”

I went through this phase too. You’ll eventually realize that visual editors are simply less efficient than text editors for programming purposes. The boxes convey less meaning than symbols in a text editor. The boxes and connections are also symbols, but they take up far more screen space than text, so you see much less code simultaneously, which makes the box editor less efficient. Also, there aren’t usually restrictions on the layout of the boxes, so it’s very hard to follow the “flow” of code in a visual editor. In a text editor, the flow of code within a function is at least mostly linear (left to right, top down).

Brandon Bloom

I’m not talking about “boxes and connections” I’m talking about representing the actual code blocks. Take a look at this tutorial: http://www.jetbrains.com/mps/docs/tutorial.html

warjan

I think you are right saying it is too much. Especially application developer might find MPS to be too much for achieving common programming task, but for library/frameworks and DSL creators it is IMHO very valuable proposal. It could be also good for providing DSL for clients to cusotmize products.

Brandon Bloom

“good for providing DSL for clients to cusotmize products”

Maybe if your clients are hardcore engineers, but in general asking clients to write code is a tough value proposition.

However, let’s assume your clients are hardcore engineers. Not only does MPS ask them to learn a DSL, but it is also asking them to learn a new editor.

One step at a time.

Break the Cycle of Broken Builds

Broken builds suck. Breaking the build sucks. No one wants to have their work stopped by others’ build breaks and no one wants to cause work stoppage on account of their own build breaks.

So then why do builds break so often? You’d think that after so many years of software development, someone would have solved this problem. Microsoft employees tens of thousands of engineers across hundreds, if not thousands, of code projects. Many of those code projects are development tools for the engineering community at large as well as engineers within Microsoft. Yet, still, all of the tools at the disposal of those engineers are highly prone to build breaks.

Below, are three ideas for reducing build breaks. Together, they could all but eliminate the problem.

Use a better version control system

Everyone keeps raving about distributed version control, and for good reason. I won’t rehash the argument here, but I will say: branch early and merge often. Perforce (better known as Source Depot within Microsoft) and it’s spiritual successor Team Foundation Server are completely unbearable . A great majority of build breaks can be contained if people are only pulling known good changes from dedicated integrators. Everyone checking in all at once to a main branch simply doesn’t work with anything but the most tightly knit of teams. Even if the does build break, it should only block the person waiting on that particular change.

Isolate build breaks

sd submit -c 12345 translates roughly to “I’m 100% confident that my changes are solid and am equally confident that I am submitting the changes that I think I am.” You never can be 100% confident, don’t take any chances: submit to a private branch which is being monitored by a build service. The server should detect your submission, run a full buddy build (with unit tests!), and then submit it to the shared branch only after it has passed.

If your builds take too long for this, people should be able to grab changes down from your unverified branch on an as-needed basis. If this happens too frequently: your builds take too long. Refactor your system into smaller components and formalize the contractual interface between those components. Then buddy build just the components which have changed. Verify that the interface hasn’t changed with a unit test. This will stop compile errors at the boundaries.

Go lazy, be late

This one is a big of a pipe dream… software systems developed with dynamic languages do not experience build breaks nearly as often as those developed with static languages. This is because dynamic languages are typically lazy-loaded and utilize late bounded method invocations. If someone adds a new button to the UI which calls a method in a file they forgot to add to source control, that should only block people who want to click the button!

There is no firm technical reason why statically or JIT compiled systems can’t simulate this. Most compile errors could theoretically be treated as a runtime error. Clearly, compile errors are valuable, but should be ignorable whenever possible. Don’t ignore them in your known good builds, but ignore them when they stand between you and forward progress.

Since most of us aren’t writing our own compilers are full tool chains, here is some more practical advice for projects using statically compiled languages: write code which fails gracefully. Runtime errors can be build breaks just the same as compile errors, but try to defer the negative consequences as much as possible. One broken feature shouldn’t break the whole project; unless, of course, you are about to ship. In production builds, perform verification code at startup or in unit tests, but try to be lazy and late in development builds.

Contributions

The modern, read/write web thrives on processes which accept and coordinate a large set of contributions from a set of contributors that is almost as large. Before this was common on the web, open source software capitalized on the aggregate efforts of communities. However, not every web 2.0 site or open source project succeeds in attracting or retaining contributors. In the case of web sites, this is often due to poor user interface or other highly visible problems

Software projects, however, typically suffer from a different problem: the cost of creating and integrating contributions. This ailment also afflicts proprietary software, where it is actually rarely diagnosed, and even more rarely treated.

I argue that all substantial software projects should strive to minimize two variables:

  • Aggregate cost adding a contributor [1]
  • Maximum individual cost of accepting a contribution
  • If either of these variables are not minimized, a project will fail to fully utilize the efforts of its key members, never mind begin to capitalize on the long tail of contributions. High contribution costs waste countless millions of hours of productivity.

In a company, the easiest way to measure the cost of adding a contributor is to measure the time from signing the employee agreement until the moment that the software builds and runs on the new-hire’s development machine. Don’t forget to count this in man-hours because this will surely require the efforts of a manager, a more senior engineer, or both.

Additionally, you should also count the time before the new developer feels some level of confidence in the codebase. Can this new person dive right in and start hacking on a new feature? Could they fix a bug without everything falling apart? How many times will they need to knock on the door of the expert? [2]

Measuring the cost of accepting a contribution is subtly different. The aggregate cost is not of critical importance as long as it is justifiable given the benefit of accepting a contribution. However, minimizing the aggregate cost of accepting a contribution is already the goal of any successful software project. Teams are always striving to be more efficient, but it is a local optimization. If you want to increase the number of contributors and contributions, you need to minimize the maximum cost to maintainers, newcomers, other team members, and communities.

As any good manager knows, they must suffer the pain of meetings, accept frequent context switching, and act as a shield from politics. They must do all of this to ensure their reports are as productive as they possibly can be. Similarly, senior engineers should donate some of their time to analogous tasks in component and systems ownership. Although these tasks introduce a cost which causes an individual contributor to spend less time actually contributing, it opens the door to greater net contributions.

So here come’s the concrete advice part of the post…

Some ways to reduce the cost of adding a contributor

  • Buddy builds – One button should instantly and automatically allocate a fresh machine, enlist in the source tree, build without additional dependencies, execute all tests, and provide detailed status and results.
  • Standardized tools – Allow contributors to transfer their skills in version control, issue tracking, building, testing, and communication. Every team is different, but the more you can reasonably share, the more you simulate one cohesive team.
  • Encourage branching
  • Understand that contributions can come from anyone [3]
  • Keep documentation up to date and complete – Hold new project members accountable for the correctness and completeness of the newcomers wiki page.

Some ways to reduce the cost of accepting a contribution

  • Automated unit tests – Insist on a high code coverage and a low flakiness rating. Require a 100% pass-rate before committing to the main branch.
  • Explicit coding standards
  • Code reviews
  • “Lieutenants” – Put an “owners” file in each directory of your source tree. Fill it with the usernames of those who should perform the code reviews for those files and sub-directories. Keep these files up to date.
  • Widely and clearly communicate project vision and direction – This ensures misguided contributions will be rejected swiftly.
  • Regular release cycle – Do I really need to say “release early, release often”? Releasing often enough will force you to reduce the cost of your release process as well.
  • Got any more?

[1] This post was prompted by my startup hobby project’s failure in this respect. Adding a third team member took several hours of my attention, but should have taken several minutes.

[2] Sorry Stephen!

[3] Some discussion lists at Microsoft are notorious for responding to internal feedback with the URL http://career/ and possibly a snarky comment. These people are artificially setting the cost of adding a contributor to be a career left-turn and the cost of accepting outside contributions to infinity. I hope these dinosaurs go extinct.

Rift Is Available on NXE!

You can download the demo or purchase the game on the web or from the Community Games section of the Games Marketplace on your NXE dashboard. If you get it on the web, it will automatically be downloaded to your Xbox 360 the next time you sign in.

Sure, the game isn’t worth the $1.75, but you can still pay for it anyway because you are super generous. And sure, the game has serious design flaws because it was put together in three days as a homework assignment. And sure, the game is loaded with performance issues because it was ported to XNA Framework 3.0 and “polished” for release in two evenings involving beer. But you are super generous.

Definitely check it and all the other great XNA Community Games launch titles. They all have free demos. Exciting times!

Control Flow Has to Flow

I’ve been experiencing MSBuild a bit at work lately. Ignoring the mortal sin of designing a programming language within XML, MSBuild fails on usability in another fundamental way: control flow has to flow.

In general, there are two key approaches to control flow. One approach is the procedural “jump” approach. This includes all manner of conditionals, loops, and more. The second approach involves functional constructs such as recursion and pattern matching. These two approaches are blended between procedural languages, functional languages, and everything in between and beyond.

The procedural and functional mechanisms can be visualized in terms of forwards and backwards. Procedural control flow moves forward in a set of steps which are executed in sequence as defined. Functional control flow begins with the results and works its way backwards pulling in bits and pieces of data and logic along the way.

MSBuild uses neither of these approaches.

That was a lie. MSBuild targets are executed in sequence procedurally. Targets can also define a “DependsOn” property which works functionally to pull in pre-requisite tasks.

The truth is that MSBuild introduces (and often requires) a very unwelcome third approach in the form of the “Condition” property. The “Condition” property is neither forwards nor backwards control flow. It is sideways. MSBuild has only one global namespace of variables (confusingly, called “properties”) which can be set or tested at any time.

With either a procedural or functional control flow, you can follow the flow simply in your text editor. In a structured procedure, you simply trace through the code pretending to be a human interpreter. In a functional method or when dealing with sub-procedures, you can trivially search for functions by name as they are called. In a sense, you are “flowing” through the code like water though pipes. You are either pumping water through or sucking water out.

With sideways control flow, you need to search for all references to every variable at every step of both the procedural and functional code flows. This is a laborious and error prone operation because after finding a relevant “Condition”, you need to analyze the forward and backwards control flow which led to that sideways control flow construct! There is no “flowing”.

Sideways control flow leads to a combinatorial explosion of forward and backwards control flows. The developer is required to keep all of these flows in their heads all at once. Meanwhile, in normal programming languages, you only ever need to deal with just two: forwards and backwards.

Imaginary APIs

As promised, I will explain my favorite way to design APIs. I call it “imaginary APIs”. I’m far from the first person to have thought of this and I’ve even seen this used in practice by others, but I have no idea if there is an official name for it.

In short, the idea is that you write code which consumes an imaginary API and then you imagine it works. Then, you write some more code which does something more advanced with your imaginary API and imagine that works too. Then, you figure out how to get your magic code to compile, run, and work.

Success is measured by how closely your final API matches your imaginary API and by how much you need to modify your magic code to be a working sample you can ship along side your API. That’s it.

As an example, let’s imagine an API for a simple home movie editing program.

var video = new Movie();
video.StoryBoard.Add(Image.FromFile("title.png"));
video.StoryBoard.Add(new CrossFade(TimeSpan.FromSeconds(2.0f)));
video.StoryBoard.Add(Movie.FromFile("scene1.wmv"));
video.StoryBoard.Add(Movie.FromFile("scene2.mpg"));
view.Export("myMovie.avi");

A lot of voodoo is going to have to happen behind the scenes to make this code work! But you will thank yourself when you need to implement the UI and your client developers will thank you when they want to generate a highly customized slide show or develop a nifty flip book drawing application.

Imported Comments

Doug Jones

I think you might be able to consider “imaginary APIs” as a type of test driven development.

No matter what you call it, I think it’s a good way to start developing a clean API.

(On a random note, the captcha for this comment is ‘ggsux’. Is Google self bashing now?)

Brandon Bloom

It definitely is a form of test driven development; good call.

However, I think the key difference is that real tests need to compile and run. Imaginary APIs can exist in an email or document and never actually need to be compiled. But yes, the next step from here is unit tests :–)

Simplicity Belongs in APIs

In a recent post, I insisted that flexibility belongs in APIs.

Simplicity also belongs in APIs. There are two key things to simplify in APIs:

  • Common tasks
  • Surface area

Since you are jam packing your APIs with loads of powerful and flexible functionality, you might have a lot of functions and data structures. You may have hoards of configuration options or capability bits, and a whole mess of operations to perform on them. However, your UI is simple and has picked sensible defaults for every operation. Your APIs should do the same. In fact, you should expose the same simplified operations your UI uses, so that developers do not have to guess what arguments you pass or in what sequence you composed more complex operations. If you have widely used private helper methods you’ve developed for your UI, you have identified common tasks for which your helper methods should be promoted to address.

Developers should be unwittingly led to these simplified methods, they shouldn’t have to go searching for them. For example, the .NET framework provides a significant number of overloads for opening a file. The most complex of these takes parameters for file access, sharing, creation behavior, and more. The simplest of these, just opens the damn file. Reading all of the lines from a file is a very common task that involves opening a file, iterating over each line, accumulating the results, and then closing the file. Or you could just call File.ReadAllLines and simply get an array of strings.

ReadAllLines is completely implemented using StreamReader, but was placed in the File class instead of the StreamReader class because more people would look there first. Organize APIs by how naive developers would look for them, not experts. Remember that developers, like users, are perpetual intermediates. They are only going to write the code once (as beginners) and iterate on it several times until it works (as intermediates). After that, they will move on to something else. There won’t be time for them to become experts.

You have limited screen space for features, but you can always fit more APIs. You develop a great UI by removing the features you don’t need. However, you develop a great API by avoiding adding the features that you don’t need. If there is a set of parameters no one will ever change, remove the ability to change them. If there are multiple approaches to the same common task, choose one. The less surface area there is for a developer to search and understand, the more likely they are to quickly choose and implement an effective solution.

In a future post, I will discuss my favorite technique for designing APIs.

Why So Serious?

Software doesn’t need to be serious all the time. Of course, this applies to games, software toys, and consumer focused products, but it also applies to enterprise class software.

Compare Gmail’s empty spam folder message:

Hurray, no spam here!

Versus Outlook’s:

There are no items to show in this view.

Which message would you prefer to see each time you open your spam folder? Google’s, of course.

Does Google’s choice of words cause you to question the quality of their software? Do they cause you to question whether or not you can do serious work with their tools? Of course not.

Just because you are doing serious work, doesn’t mean you need to be serious all the time. Research, anecdotes, and all the other evidence clearly show that a little bit of humor and personality injected into the workplace improves productivity and moral. Same goes for your software. Inject some personality. Include some humor. Everyone will be better off.

Bug Farms

I recently learnt a new microspeak phrase that I love: bug farm.

Some features are bug farms.

This means that these features are either poorly specified or highly unmaintainable. As a result, they produce a larger than average portion of the product’s bugs. They can get into either state though a variety of ways, but by far the most common way is insufficient thought during design or implementation. Additionally, it is also possible to establish a bug farm by building a feature on top of an existing bug farm.

If you encounter a feature which is covered in larvae, you should spray it with insecticides immediately. It is better to use too much insecticide and kill the feature than it is to deliver an infested product to your customers. That is, if the bugs don’t rise up and eat you before you get the chance to do either.

Flexibility Belongs in APIs

Software comes in a large spectrum of power and flexibility, as well as an equally large spectrum of complexity and usability. Users also come in a wide range of intelligence and skill. However, if you create software of average power and average complexity, you may satisfy the average users’ needs, but they won’t love you for it.

One of my favorite books on software, The Inmates Are Running the Asylum, describes the concept of “perpetual intermediates”. The book explains that beginners are only beginners for a little while, then they become intermediates. Most users are intermediates for as long as they use the software. Very few users become true experts. At any given time, virtually all of your users will be varying intermediates.

With this powerful realization in mind, you may be tempted to design exclusively for these intermediates. When designing your software you can spin the flexibility and complexity dials to try to hone in this average user. Unfortunately, you would wind up pleasing more users by cranking these dials down much lower.

The fundamental problem is that adding a bit of power and flexibility also adds a bit of complexity. Since most users are intermediates, they can tolerate some degree of complexity, but would honestly prefer not to. They avoid the complexity by only using the small subset of features they have come to understand and only venturing beyond their comfort zone when they absolutely have to. They quickly become intermediates and are no longer troubled by complexity.

When users have to venture into uncharted features, they are effectively beginners, not intermediates. However, the more esoteric or powerful the feature, the more likely it was designed for that small percentage of experts. When the infrequent need arises for these features, users would rather force the problem into the features they know than learn new ones.

Given that users actively avoid complexity and more powerful features, it becomes apparent that these powerful features should be excluded to keep complexity to a minimum. In doing so, you may annoy the 1% of your users who are truly experts and need the power. If you have a million users, 1% — 10,000 users — can not be forgotten. You need to give these users this power somehow.

Put the power and flexibility into APIs! APIs enable power users to automate frequent tasks and create the unique functionality they need. As a bonus, new tools will pop up to fill needs you didn’t even consider when you originally designed your software.

“Wait a minute!” I hear your cry. “Expert users are not necessarily programmers”, you say. This is very true, but there are two factors which mitigate this problem.

First, most expert users of any software have at least some technical ability with which to hack together solutions. If you don’t believe me, take a look at the popularity of Visual Basic. Despite the relatively small number of professional software engineers using Visual Basic, it still has very strong search volume and book sales. This is presumably due to its inclusion in Microsoft Office. However, the “real” software engineers view it as not worth their time and these are the people producing technical content on the web. As such, there is less online discussion of the language. Even if an expert is unable to put something together, they probably know someone who can. And even if they don’t know someone who can, then they probably have just identified a need to hire someone.

Second, the needs of experts are so specialized that they need to exert a large amount of effort bending flexible software to their needs. Sometimes, no amount of flexibility will solve their problem. Other times, the flexibility is hidden behind so much complexity that it is not cost effective to uncover it. Still other times, users bend software until it breaks. Again looking at Visual Basic, it is quite common for Office users to create macros or scripts to solve problems that built in tools could have solved. Users do this because they didn’t know they could or couldn’t figure out how to utilize the existing features in the way they wanted to.

The bottom line is that software should be as simple as possible, not simpler. Treat users as intelligent, capable intermediates; do not engage in excessive hand-holding or assume expertise. Where applicable, provide plug-in or extension mechanisms, so that power and flexibility can be leveraged by APIs. Your users will love you for it.