diff options
Diffstat (limited to 'Readme.md')
-rw-r--r-- | Readme.md | 202 |
1 files changed, 159 insertions, 43 deletions
diff --git a/Readme.md b/Readme.md index 260bbdea..a55b78b0 100644 --- a/Readme.md +++ b/Readme.md @@ -1,5 +1,121 @@ **Mu: making programs easier to understand in the large** +Mu explores an unconventional hypothesis: that all the ills of software +directly or indirectly stem from a single historical accident -- automatic +tests became mainstream *after* operating systems and high-level languages. As +a result the foundations of modern software have fatal flaws that very smart +people have tried heroically and unsuccessfully to fix at higher levels. Mu +attempts to undo this historical accident by recreating a software stack from +the ground up with testable interfaces. Unlike prior attempts, the goal isn't +to create a perfectly designed programming language and operating system (I'm +not smart enough). Rather, the goal is to preserve optionality, to avoid ever +committing to a design decision, so that any new lessons yielded by experience +are always able to rework all aspects of the design. The goal is to learn to +make software *rewrite-friendly* by giving up on backwards compatibility. + +*The fundamental problem of software* + +We programmers love to chase after silver bullets, to argue the pros and cons +of different programming languages and tools and methodologies, of whether +rewrites are a good idea or not. When I see smart and reasonable people +disagreeing on these questions I often track their difference down to +variations in personal experience. In particular, people with good experiences +of X seem disproportionately to have tried to use X at a smaller scale or +earlier in a project's life than people with bad experiences with X. I surmise +that that difference explains the lion's share of the benefits and drawbacks +people observe. It doesn't matter what programming language you use, whether +you program functionally or not, whether you follow an Object-Oriented +methodology or go Agile, whether you use shared memory or the Actor model. +What matters is whether you did this when the project was relatively early in +its life; how many man-hours had been spent on it already before your +alteration; how many people had contributed and then lost interest and moved +on, taking some hard-won unique knowledge of the system and its users out the +door with them. All projects decay over time and get slower to change with +age, and it's not because of some unavoidable increase in entropy. It's +because they grow monotonically more complex over time, because they are +gradually boxed in by compatibility guarantees, and because their increased +complexity makes them harder and harder for new team members to understand, +team members who inevitably take over when the original authors inevitably +move on. + +If I'm right about all this, one guiding principle shines above all others: +keep projects easy for outsiders to navigate. It should be orders of magnitude +easier than it is today for a reasonably competent programmer to get your code +up and running, identify the start of the program, figure out what the major +sub-systems are and where they connect up, run parts of your program and +observe them in action in different situations with different inputs. All this +should require zero hand-holding by another human, and it should require very +little effort spent tracing through program logic. We all have the ability to +laboriously think through what a function does, but none of us is motivated to +do this for some strange program we've just encountered. And encountering a +strange program is the first step for someone on the long road to becoming a +regular contributor to your project. Make things dead simple for them. If they +make a change, make it dead simple for them to see if it breaks something. + +But this is a hard property for a codebase to start out with, even harder to +preserve, and impossible to regain once lost. It is a truth universally +acknowledged that the lucid clarity of initial versions of a program is +quickly and inevitably lost. The culprit here is monotonically growing +complexity that makes it impossible to tell when some aspect of the program +grows irrelevant and can be taken out. If you can't easily take something out, +you'll never do so because there'll always be more urgent things you could be +doing. + +A big source of complexity creep is your project's interface to external +users, because you can't know all the ways in which they use the services you +provide. Historically we react to this by assuming that our users can do +anything that we ever allowed them to do in the past, and require ourselves to +support all such features. We can only add features, not drop them or change +how we provide them. We might, if we're forward thinking, keep the project +stable for a time. But the goal is usually to stabilize the interface, and +inevitably the stabilization is *premature*, because you can't anticipate what +the future holds. Stable interfaces inevitably get bolted-on features, grow +complex and breed a new generation of unsatisfied users who then attempt to +wrap them in sane interfaces, freeze *those* interfaces, start bolting on +features to them, rise and repeat. This dynamic of interfaces wrapping other +interfaces is how unix cat grows from a screenful of code in the original unix +to <a href='http://landley.net/aboriginal/history.html'>800 lines</a> in 2002 +to 36 *thousand* lines of code in 2013. + +To summarize, the arc you want to avoid is: you make backwards compatibility +guarantees → complexity creeps monotonically upward → funnel of +newcomer contributions slows → conversion of newcomers to insiders stalls +→ knowledge of the system and its rationales gradually evaporates → +rate of change slows. |