Wednesday, October 10, 2012

Knowledge Bases

To me ‘intelligence’ is basically raw human thinking power. It’s our ability to work through problems. In order to employ intelligence effectively in our world, we need data. And that data needs to be structured and interconnected to make it usable to us. Usable, organized data is what I take to be ‘knowledge’. Its all ready to be utilized.

Our various endeavors are categorized into a huge number of fields (or domains) such as law, medicine, finance, physics, math, biology, etc. each of which is mostly an independent ‘base’ of knowledge about the field. What’s really interesting about most knowledge bases is that they are easy to over-simplify and also frequently counter-intuitive in their depths. An outside perspective can easily lead to the wrong conclusions. It’s not until you are steeped in the details, that you’re able to apply your understanding correctly.

Without enough underlying knowledge, intelligence is just hand waving. You can think about something as deep and as long as you want, but your conclusions are unlikely to be reasonable.

Software development is extremely complex. Not only do you need a strong understanding of the underlying technologies and how to use them, but it also cuts across many other knowledge bases.

A software system is a solution to a problem in a user domain. If it doesn’t solve their problems, then it is only adding to them. It consists of a huge number of details, all of which need to be organized and fit into the final work. Managing these pieces, usually under tight time-lines requires a special set of skills. Software is also very expensive to build and maintain, so finding the money to pay for enough resources is always tricky.

Putting this together we see that software spans the following knowledge bases:

  • Technology
  • User domains (all?)
  • Management
  • Business

Most programmers tend towards believing that technology is the most significant issue in software development, but usually the problems start in other areas and feed back into the development work. For this post, I’ll go through all four areas in the order that I find have the most impact for big projects.

Management

A large software project has millions of details that are all being juggled at the same time. Keeping most of those balls in the air, without losing track of them, is necessary to deliver a working system.

A good manager is constantly running around, making sure that all of their resources are moving forward, and that any roadblocks are eliminated as swiftly as possible. Some people see software management as a higher creative input role where they just have to inject ideas, but that’s never actually helpful. Most development projects have more than enough ideas, what they need is organization, process and to keep everyone on the same page. In that sense a good manager accepts their role as herding all of the cats in the same direction, while making sure the path forward is clear.

Development meetings are a good place to assess management. If they are retrospective and basically just a forum for people to catch up with what has already happened, then the project is not well-organized. It shouldn’t be necessary for the manager to catch up, if they have been paying attention as they should.

If, however, meetings are really planning sessions for the next upcoming work, then they contribute by identifying the issues long before they become significant enough to derail the process. Plans don’t always work as expected, but planning is a necessity when you are dealing with long running work. Even small tasks in development can take weeks or months, and the worst possible outcome is to keep shifting directions before any of the work is completed properly. What’s started should be finished, or it shouldn’t have been started in the first place. Only a long-term plan will avoid time-crushing ‘make-work’.

Since development flows through different phases, the management of each has to change as well. A basic development iteration has five parts:

  • Analysis
  • Design
  • Implementation
  • Testing
  • Distribution

Analysis for example is a never-ending effort about depth. Wherever you skimp on analyzing the requirements, it will come back to haunt you as ‘scope creep’. However it would be rare to actually have enough time and patience to fully analyze everything before the work starts. Analysis is also very sensitive to the approach; ask the right questions and you get the right answers. Ask the wrong ones and you get misleading information. Managing analysis means ensuring that both the right questions are being asked, and that the answers are being organized. Experienced analysts need little intervention, but the quality of the work isn’t known until later in the implementation phase, when it is often too late.

Design and Implementation are really about making sure all of the programmers are on the same page; that they are working together well and that they have all of the input necessary to keep them moving forward. For large projects this is basically about team building. A system built by a dysfunctional group is naturally a big ball of mud, and the messiness of the work creates a huge of amount of extra make-work to keep it all from falling apart. A good manager needs to keep the team working, arbitrate disputes and enforce the process and standards even when the time pressure is intense. They also need to protect the programmers from any outside interference to insure that they have enough calmness to be able to think clearly and deeply about their work.

Testing is all about quality vs. time trade-offs. It is impossible to test to 100%, so there is some lower percentage of testing that has to be accepted. Often that might even be just a fixed block of time. The biggest problem during testing is to keep everyone working. Testing is often seen as boring -- particularly after an intense development slog -- and the developers start to lose their attention to the details. Bugs get noticed, but then forgotten. Managing here means dragging people around, holding hands, calming nerves and just trying to get the process as constructive as possible. It can also means having to make brutal choices about quality vs. timeliness. Sometimes the software has to be shipped, even if it is not as good as it should have been.

Distribution of software usually involves an operations dept., or selling the system. Because it is usually forgotten until the end, the distribution is rarely well-planned. Management here needs to insure that the software is supported well enough, but not at the cost of disabling future development work. The handling of feedback and bug reports can all be set up in advance, and most projects require both a standard release and an emergency fast-track process. Most issues take some time, but some need an ‘all hands on deck’ approach in order to deal with them before the situation escalates.

Business

A large software project takes many man-years to build and for most projects the work is never done. It just goes on, year after year. Hiring programmers isn’t cheap and you need a lot of other support staff in the project as well including: project manager, system admin, specialists, etc. Commercial quality systems also need roles like: graphic designer, UX expert, editor, translator, etc.

All together, to build something large that isn’t a weird eclectic mishmash of disorganized functionality means having to shell out a huge amount of money. For software, money is time. That is, if you have enough money, you can hire the necessary resources and experience to get the work done, given some reasonable time frame. A lack of money, means that you have to do more with less, and often times it’s that time pressure that leads people to take ill-advised shortcuts, which will eventually scramble the project so badly it can’t be saved.

As such, setting up a development project always means having to figure out where the money is going to come from. And as is always the case with money, what strings are attached to it. Nothing comes for free.

The business world is very subtle, complex and constantly changing. Most techies who encounter it greatly oversimplify its nature, shades of gray and depth, which generally leads to unrealistic expectations of how things ‘should’ work. Some people have a better intuitive feel than others, but for most people it is best to just write the whole domain off as ‘irrational’ so that they won’t make any false assumptions.

Because it is volatile, the business world inflicts a lot of short-term influence on software development. This conflicts with the long-term nature of the work, so it requires making a large number of very difficult trade-offs. If the work always bends to the short-term concerns it will quickly become a mess. If it always sticks to the long-term, it will likely lose confidence and funding. Thus it needs to perform a very difficult balancing act between the two, so a good leader that finds the right trade-offs will end up taking flak from both sides. If both sides are a little unhappy, then it’s likely balanced properly.

For most people, the ability to balance the business and technical requirements is learned from long, hard and brutal experience. Intuition is usually bias to one side or the other. Even the greatest intelligence can only ‘guess’ its way through the complex interactions, getting more wrong than right. So all that is left is learning from experience; from the successes and mistakes of the past. And it takes some painful introspection to really be objective about the many causes of failure. People like to blame others, but within the spidery web of development, all things are related and no one is immune from influence.

User Domain

Software starts with a problem that needs to be solved. That problem is always domain specific, such as a financial system, inventory or even social networking, etc. Most domains have their own unique set of terminology, usually steeped in history. They often have ‘rules of thumb’ or dirty little secrets lurking in their darkened corners. What they never are, is laid out in a nice rational manner all ready to be modeled in software. Often the data is poorly understood, the process disorganized and each organization within the domain is slightly different. There are always rules, but they are not always followed rigorously, nor particularly logical.

Thus the core problem in software is taking some ill-defined ‘informal system’ and finding a reasonable mapping to a very rigid formal system modeled inside of a computer. In practice this makes the analysis of an existing domain one of the most crucial parts in arriving at a functional software system. However, because it is always gray and messy, it is also the part of the process that people ignore the most often. They just start coding, and hope that somehow, after a while, the answers will come to them. Sometimes that works, but more often getting off on the wrong foot is extremely fatal. If you build too far from the actual answer, then you’ll have no choice but to do it all over again. But since people don’t like to restart their efforts they usually flail at the code, hoping it will somehow work itself out. However if they started really badly, they could bang on it forever and still never get something that works.

A very common problem in development is that the users rarely know what they want, or what would work correctly for their problems. Some have strong opinions, but they often lack a full understanding of the consequences of their choices. Most flip-flop faster than the development can be completed, so tying the process too closely to their wishes frequently results in a mess of half finished, or poorly thought out code. However the converse is also true, code written in an “ivory tower” far away from the users most often oversimplifies the core problems making it somewhat less than helpful. To bridge this gap requires domain experts, and often a very deep understanding of the real problems. Everything the users say is important, but not all of it needs to be taken literally. They usually understand their own informal systems, but the mapping to software, and the code itself are best left to people with plenty of experience. It is easy to write code, but extremely difficult to know what code is right for the solution.

Technology

Last, but not least are the technologies being used. Most projects involve anywhere between 3 and 30 major technologies, and a slew of minor ones. Each technology is eclectic in its own unique way and needs some time and experience to discover its strengths and weaknesses. People often focus on the programming language when assessing skills, but in most projects crafting conditionals, loops and slicing & dicing code are the easy parts. There is skill involved in solving the endless array of coding puzzles, but unless the project is pushing the bleeding edge of technology, it becomes significantly easier as one gains experience. Unfortunately, getting heavily seasoned (>15 yrs) programmers is difficult, since they are expensive and often leave the industry.

Software development is hugely affected by scale and by the desired quality of the final work. For scale I usually break it down as:

  • small - 1 developer, <30 lines="lines" span="span">
  • medium - 1-4 developers, <99 lines="lines" span="span">
  • large - 5-20 developers, <1 lines="lines" span="span">
  • huge - teams of developers, millions of lines

Projects don’t jump scale easily, they often require a nearly complete rewrite to go from one size to another. Disorganization and bad practices often work OK for small and medium projects, but they become fatal beyond that.

As well as scale, the desired quality is important. I generally see it as:

  • prototype - rough proof of concept
  • demo - a small working set of features, mostly works.
  • in-house - eclectic, lots of rough edges, inconsistencies and operational issues
  • commercial -- solid, dependable and beautifully designed by graphic designer / UX experts. Looks good, works correctly.

Each increment in quality takes considerably more work and requires specialists to focus on their particular strengths. Products often sell although they are just in-house quality, but then they are vulnerable to stronger competitors. Users may not complain directly about inconsistencies, but they do generate a negative impression of the system. Badly architected ‘balls of mud’ can often degenerate in quality as programmers randomly slap weak functionality into the corners. Poor development practices tend to be reflected in the interface, so often the outside of the system is a good indication of the state of the code base.

A rather dangerous development trade-off often comes in the choice to build or buy (paid or free). Depending on the technical specs, building is often extremely time-consuming, but in my career I’ve seen more people fail because of their choice to buy. All technologies are a collection of their author’s eccentricities, and often these play a dominate role in their usage. Buying specialty libraries and packages usually works well because you don’t have to acquire the knowledge to build them, but for the core parts of the system,if you depend on someone else’s solution you limit your options going forward. That can drive the architecture and constrain the path forward in dangerous ways.

Getting a commercial grade large or medium system to users is always a team exercise. It takes a large group of people, all with different specialties, to make it happen. As such, the team dynamics become crucial in determining the outcomes. Badly functioning teams usually produce badly functioning systems. Rogue programmers may get a lot of opportunity to express their creativity in their work, but they often do it at the expense of the overall project. Multiple different coding styles and no consistency generates high bug counts and stability problems. Once a project gets going it only continues to work if all of the people involved are on the same page, which generally means strong leadership, a reasonable process and a well-defined set of objectives. Efficiency means long-term goals, even if the short-term is volatile. Initial speed can be gained from brute force hacks, little or no reuse and heavily siloed programmers, but each of these accrues significant technical debt, which eventually bogs the project down into a mess. If the work is getting harder over time that often means disorganization, no architecture, redundancies and/or no long term plan, which if left unchecked will only get worse. A well run, well thought-out development effort will build up a considerable number of reusable common components, which if documented will guide extensions and new features. Architecture sets this commonality and management enforces its usage.

Finally

What sets software apart from most other professions is that it requires a larger cross-section of other knowledge bases to keep it successful. The danger in software is that from an outside perspective, it all looks so easy. You just have to throw together a bunch of simple instructions for the computer and chuck it all onto a server. But that type of over-simplification has always lead to disasters, caused by people who don’t have enough experience to respect the underlying complexity and trade-offs required for successful development. Most other professions are usually managed by people who have moved through the ranks. Software is often special, in that the most experienced developers are rarely put into full leadership positions. More often it is business people or domain experts that attempt to drive the projects forward, although few have the necessary prerequisites. Lack of knowledge usually equates to bad choices, which always mean more work and a much higher likelihood of failure. The complexities, knowledge and work involved in software development are easy to underestimate, so the failure rate is obscenely high.

Sunday, September 30, 2012

Serializations

For the purposes of this post I’m going to over-simplify things considerably by saying that all of the stuff that people learn get stored in their brains as a series of ‘models’. A model in this case is essentially a large set of ‘symbols’ and a whole lot of ‘interconnections’ (relationships) between them. Structurally we could view this as a graph (from graph theory), or we could see it as a relative multidimensional mesh of some sort. It doesn’t really matter for this post, either perspective will do.

As we progress through life we collect facts and relationships which help to build up these models. Sometime, we cross-link them to each other forming larger models, although there is no requirement to do so. We can have many models that are essentially independent from each other.

So what we have in our minds is heavily interlinked multidimensional data that is used to drive our actions and behaviors. Often we want to communicate parts of this to the others around us. In order to accomplish this, we pick a starting place in a model and then traverse through the nodes in some complex fashion. We take these paths and we ‘serialize’ them into a stream of sentences which we speak out loud. No doubt the whole process is significantly more complex than what I am describing, but the important aspect is that we are linearizing our vast internal models down to a string of ‘symbols’ that we then communicate to others, who as they listen hopefully update their own internal models with what we’ve sent.

The fact that we are serializing paths through our models has a huge number of interesting consequences. An obvious one is that there can be many many different paths through a model. We see this quite easily because often different people will find very different ways to express the same thoughts. Often too we can see from the paths, that two people may have similar models, but they differ in parts. It isn’t always obvious whether the differences come from the model or from the serialization.

Another consequence is that people listening to the stream (or reading the stream) don’t always update their model or they possibly update it incorrectly (due to lack of attention, ambiguities or other interference). And of course, we do hold multiple conflicting models in our heads precisely because we are seen to so frequently have contradictions in our own understanding. Things we’ve learned in one context can contradict things we have learned in another. If we become aware of that, we could possible intertwined or merge the models, but we not always aware of these contradictions.

The serialization output itself is interesting because we have found many different ways of representing aspects of these models. The most obvious is that we use different verbal languages. Within languages we also break out into different subsets of terminology that are specific to a domain, such as law, computers, medicine, etc. As well, we have learned to serialize aspects in more fundamental formats such as music, painting or poetry. These ‘rawer’ forms generally communicate deeper aspects of our models that are oriented around our underlying emotions. We also have more rigorous representations such as mathematical notation, which we’ve broken down into subsets we call ‘branches of mathematics’ (each often associated with one or more formal systems). People commonly see these types of ‘formal’ serializations as the ends themselves, but really they are just linear representations of multidimensional models that happen to be abstract formal (rigid) systems. Reading and understanding a string of new mathematics results in an update into what we know internally in our models.

One of the most fascinating achievements of the twentieth century was our success in instantiating our own abstract formal systems. Prior to this, mathematics produced models in our heads and we used them to help us explain the world around us. They didn’t have physical manifestations, although we could serialize them to paper and pass them on. But once Alan Turing actuated the notation into a construct that we could create physically, at least one of our formal systems left the abstract world and landed right into the middle of the real one. Turing Machines became physical devices and these ‘computers’ are gradually finding a place in every corner of our lives.

That this was possible reopens the question of whether or not these types of abstractions are just purely abstract or are just generalized manifestations of our reality. That’s a pretty long-winded way of saying that our thinking abilities could be entirely bounded by our universe. That we might not be able to create internal models that are purely abstract. That we can’t think outside of the box, if the box is our own physical reality. However, notions like ‘infinity’ and ‘perfect’ do appear to be disjoint from our reality, so they’re unlikely to really exist, just approximations to them. We can go a little outside of the lines, but it leaves one wondering about how far is too far?

To control our computers we create software, but in a funny sense our creation of software seems diametrically opposed to our own handling of our internal models and serialization. That is, we first create the serialization (the code), then we set it running to achieve a time-based multi-dimensional behavior. So the running software is a model just like the ones we have in our head. The code can still be transferred from one machine to another, communicating through a huge variety of ‘programming languages’ but at some point in the future it might be possible that the runtime model of the system could be updated rathe replaced and reloaded as we do currently. That shift from just instantiating new instances, over to communicating between long-running models could possibly amplify the usefulness of software systems by orders of magnitude. We’ve already started down the road to dealing with ‘big data’, but we’re still focused on the serializations and not yet the construction of massive models.

The Sapir-Whorf hypothesis suggests that our thinking may be altered by the languages we use for communication, but one might easily think that it is the other way around. Some technical subset of language contains significant localized model primitives, which no doubt drive our internal model construction in very specific ways. Thus if one becomes a lawyer and reads a lot of law, eventually they’ll start ‘thinking like a lawyer’. The primitives they communicate with are very specific sub-models that drive how they build up other models. So, if a language focuses more heavily on a eclectic subset of primitives, as it is used for communication, it will affect how people build their internal models. We see this also with computer programmers. They spend their days constructing formal systems, and they often apply that back to the informalities of the world around them giving them a rather inflexible black and white perspective on the rather grey aspects of the world around them.

Another interesting aspect to this perspective is that mathematics, as a set of communicate symbols, could be seen as no harder to learn than any other language. At the high level that is essentially true, but some aspects of formal systems do rely on difficult underlying notions; Douglas Hofstadter called them ‘Strange Loops’. They seem to contradict our build-in intuitional models, causing contradictions. However once enough of these are strange loops are resolved and understood, and are freely available to a person, the rest of mathematics really is just about depth. The models get larger (mathematics is huge), but are still communicable. A question often arises about whether ‘programming’ itself is mathematics, and this too resolves itself. Since the programming languages describe a formal system in a similar manner as mathematical systems describe a mathematical branch, both of them share a great deal of commonality. Theorems for instance, are roughly analogous to libraries of code in that they are higher representations of significant underlying detail. Larger primitives that can be welded more effectively. And since they are both bound by the same constraints as any formal system, finding complete sets of non-overlapping primitives in both models is nearly identical. But at the same time, they are also too different languages, with the same gulf between them as say English and Chinese. Knowing both is possible, but translating between them is harder (although since they have significantly less ambiguities then vocal/written languages much of the difficulty is reduced).

This perspective that we take multidimensional data and serialize it to to a linear stream provides an interesting framework around a large number of seemingly diverse interactions between people. It not only helps characterize our communications but it also gives us an insight into how we think about the world and how we share that amongst ourselves. It is a simplification, but it does help to bind together language, mathematics, computers and our own intelligence. And it gets back to that recurring underlying duality that comes up over and over again between ‘static’ and ‘dynamic’, between ‘nouns’ and ‘verbs’, and between ‘data’ and ‘code’. As we dig into our reality and generalize what we have learned we often return to the same fundamental patterns, which one could easily suspect are really manifestations of our physical reality.

Sunday, August 12, 2012

Cooperation vs. Competition

I read this excellent article in the July issue of Scientific American. It was called “The Evolution of Cooperation”. The basis of the article was centered around game theory, but the essence of it helped me to better frame my understanding of several deep issues. Often there are interesting abstract high-level constructs that once understood, nicely overlay a sea of information, leading to a better understanding.

Our modern era stresses the benefits of competition. From the earliest ideas of a ‘free hand’ that would fix people on a level playing field, we have been inundated with the notion that things would be better if we were freer to compete. Those notions have always bothered me, since inevitably in order to gain the upper hand, people bend towards pushing the boundaries of the rules. Eventually going too far. Thus it is no surprise that the Olympics needs very serious drug testing, since the rewards of winning often outweigh the risks of getting caught. Competition may start out fair, but there are always people willing to abuse it, and gradually over time as more of them do better, the rest follow. Eventually it always degrades.

So these philosophies of unfettered competition, either sound naive or they seemed to be pushed by people who are already at the margins of fairness. They’re already bending the rules and they’d like to get away with bending them harder. I do hear what they are saying, but I suspect it to either be self-serving spin or a failure to accept the full range of expected behaviors from our species.

What the article clarified is that competition really only exists on the back of cooperation. That is, if there are no rules, there are no rules to bend. There is no competition, just chaos. So in order to compete, initially everyone has to agree on a set of rules. Thus cooperation is by far the deeper principle. It must be in place first. In that sense, it seems more than obvious as a concept, after all we’ve come together to form societies, countries, companies, etc. and these entities are all held together by rules that define how one should behave within them. By sheer scale, it seems that we cooperate far more often than we compete, but if one only reads modern literature, it would seem if the opposite were true. No doubt that is because the adherents of competition are louder and more vocal than those for cooperation.

What fascinated me about the article was that their game theory models predicted that cooperation tended to reinforce cooperation. That is, if it gets root somewhere, it tends to get larger, and while the two ebb and flow with respect to each other, the formal system model tries to balance them out in its stead state. Again, not really a surprise, but it does heavily contradict those that preach progress through increased competition. It points towards that philosophy as being unbalanced (and thus unsustainable).

So far all of what I’ve said has been applied generally across the behavior of our organizations, what does that have to do with software you’re wondering? The underlying cultures of software development have always tended towards programmers getting more ‘freedom’. That’s a recurring theme and one that has always bothered me. Freedom in its simplest form is just a lack of rules. And as society shouts so frequently, a lack of rules means that it is easier to compete. So the essence of our cultural values in programming is towards individuals competing against each other in the guise of being able to push their own creative boundaries for their solutions. It’s every programmer for themselves.

That is fine when the output is limited to the amount of work a single individual can do. So if we wrote programs that were no longer than one man-year’s work of effort, the programmers would do best if they were free to code these in whatever eclectic manner they choose. However, the era of small software ended a long time ago. What we are most often building now is built on a huge number of man-years of effort. Big systems. The little stuff exists already, it is the big stuff we are struggling with.

In all the different projects I’ve worked on, one thing has always remained true. If the project is large, it will absolutely fail if the underlying programmers don’t all get one the same page together. That is, it is a team effort, and a poorly functioning team will never be successful. It doesn’t actually matter what the ‘page’ is, it doesn’t actually matter what the standards, style, architecture or technology are. All that matters is that a group of people come together and agree on how the system will be constructed, otherwise the process of constructing it quickly breaks down and the whole thing fails.

One easy way to determine who's really on a team is by what they say. The members of a team never lie to each other, even if what is being said is not pleasant. Lying is a deliberate misdirection and generally something that you might want to do to your competitors, so that you maintain an advantage over them. Telling the truth, as you know it (even if it turns out to be wrong) is what you do when you are cooperating. You are trading your understanding for theirs, so that it is shared and everyone gets on the same page. Withholding information, particularly in this case, is a form of lying. If you let an opportunity pass without speaking up about something important, then you are doing so for competitive reasons. You are deliberately choosing not to alter the direction and you are essentially lying about the fact that you don’t have any ‘other’ information to share.

Given a huge rise in expectations for software, and that the things we seek to build far exceed the complexity manageable by a single individual, getting better results for software development comes directly from getting programmers to cooperate more with each other. However, our culture (and certainly many of the discussions on the web) show that we programmers are highly competitive. We’d rather have our freedoms, and maximize them at the expense of the whole project failing. Or industry. And when that sort of problem happens, we’re quick to blame each other for the problem, rather than accepting that the root cause was a failure (on our part and theirs) to get onto the same page and cooperate with each other. We see this over and over in the industry. Excessive squabbling about how ‘theirs’ is wrong, and ‘ours’ is better. About ‘right’, and about ‘perfect’. Now one doesn’t expect to get cooperation at every level in the industry, our species would never accept that, but what we’ve seen so far is that that we are far more competitive than we should be. Everyone is out for their own personal glory (although ‘glory’ is not always defined in the same way) and as a result everyone heads out into their own unique direction, waving the banner of ‘freedom’. The consequences of this behavior have been obvious. The failure rate for software is stunningly high and there is more energy applied to telling others they are wrong, then there is for civil discussions on what would be better. The number of programmers in the field has been increasing, but the number of innovations in software (not hardware) has been dwindling. We occasionally see some interesting new ideas, but generally there are constrained to a small group of individuals. Little pockets, here and there. There hasn’t been a real major shift in technologies for well over a decade now. Just a few fragments.

So that article was really informative in laying a basis for learning how to improve things. Competition pushes us, but it also stagnates us. It motivates us, but it also limits us. If we want to move forward, then the only way to do so is via cooperation, and in order to cooperate we have to all be willing to all play by the same rules. The specifics of the rules don’t matter, just that we all play by them. It is sometimes a hard thing for people to accept, and certainly in our modern age it has become harder, but it really represents the only way to move forward. We can choose to all do things differently, but that choice also means that we’ve put a cap on what we can now do, and what we have now isn’t particularly impressive. Or we can come together to build really big spectacular things. Most of us want to build better software but in order to do that, we have to give up many of our cherished freedoms. Excellence comes with a price attached.