Friday, January 18, 2008

"Code Bloat"

Steve does a pretty good job describing what I see too with regards to code bloat. I wasted some time reading the comments to see how many were in agreement. It was less than I expected, which is unfortunate because I was hoping that his conjecture of being in the minority was wrong...

So, as the article has to do with "code compression" and the comments seem to take various interpretations of the word "compression", I will lend my interpretation as it fits in this blog.

I take it to mean "concept compression", or the ability to express consisely more abstractions. Less code doing more work. Higher levels of abstractions. He identifies "patterns" as a source of issue. Mainly in that they don't provide the right level of abstraction, instead turn into copy and paste, leading to further bloat. I can't remember if he says it directly, but a Lisp/Scheme programmer would manipulate the language, write a macro or somehow roll the pattern into an abstraction available from a keyword, or as a "property" available to all data structures that "inherit" from the macro/keyword. I think that is what he is really referring to.

I also feel his pain... For me, it isn't that the code is "bloated" necessarily, it is that the cost to maintain it is SUBSTANTIALLY larger than the initial cost to develop it. Part of that might be that the code takes on Von Neumann machine characteristics as more and more features are bolted onto it. But a more likely source of issue is related to the lack of abstraction in general. Patterns (and here I mean any pattern, not just the GoF) internal to the system aren't abstracted up a level, which is another way of saying that the dev doesn't produce more features with LESS code. (In most languages it is harder to do abstraction than copy and paste, so the dev does a lot of C&P or leans on the IDE to do it.) And I think this is the heart of what he is talking about with "compression". It is about finding a way to write the code so that you get more functionality with less code. It is also about using a tool that makes it easier for the dev to abstract than to C&P.

I have found that this is language agnostic for the most part. The trick is:

  1. Realizing when to use which language
  2. Creating the system in a waythat allows different languages to play together


But, this isn't necessarily easy with the state of the current toolsets.

Another piece of the puzzle is that smaller code bases and more abstractions usually (maybe always) mean more conceptually advanced models and topics. Which in turn means that more time goes into thinking about the model than writing the code. It is probably common for an experienced dev to spend a week thinking over the various abstractions of a problem and writing 5-10 lines of code to support it, where in the past that dev would have spent 10 minutes thinking and written 500 lines. This becomes more difficult when there is a deadline. I tend to think that regardless of the toolset, there is a flight or fight response that happens to devs when timeline pressure is added. That causes tunnel vision, which, in turn means they can't write the short version of the code, only the brute-force version. Even when a dev might know that time in design is time well spent, the panic to "just get it done" kicks in. I would be suprised to find out that I am the only dev that this has happened to.

Related is the "trainability" of that codebase. It isn't that the code is hard to read, it just requires the next dev to take a step back when they read it. Pretend that there is no deadline to learn the code and be productive.

If the code reads top to bottom in a procedural way, then it is easy to understand. Humans can grok process pretty quickly. If there are a handful of classes holding state, it is a little more complex, but there will still be a state transition graph to map the process (at least conceptually). But making the jump from (for example) a graph based implementation to a constraint solvability problem type of implementation can be a head scratcher for the person learning the code. Now the graph rendering portion of the code might be non-deterministic, or non-procedural.

I have found that humans need a picture in their minds. From the picture they start to draw the model. An analogy might be a 2-D rendering that becomes 3-D as more and more is learned about it. This is I think, one of the keys to writing large codebases and maintaining them... The model has to be simple to grok, and it has to be obvious how the code follows the model. And the model is tied in large part to the abstractions that are available. In fact, the abstractions are likely the "language" of the model.

Finally, the last point that I saw come up in the comments over and over, was the fact that he wants to do it with one person. The team isn't something that he really drills into, but there-in lies the crux of the "People who get it" and the "People who don't get it" arguement he lays out. The people who get it realize that you don't need a team to support a large number of dev efforts. In fact, it might be preferential to not have a team at all. So, if the assumption is first and foremost that there is only 1-2 people, then this is a totally different problem space altogether. If you can't throw more resources (up to a limit) at the problem, then popular opinion says that the work can't be done. It is like the more shovels you have to move a pile of dirt, the more dirt you can move. This only holds true if everyone has more or less the same toolset and knowledge level. But, suppose one day someone shows up with a steam shovel... Then it really isn't a fair comparison anymore. The steam shovel can move the whole pile of dirt alone in less time than it takes all the individual shovels.

There are plenty of examples out there, I site PG at Viaweb or RMS on the Lisp that kept pace with (was it Allegro?). RMS might be urban legend now, I would have to check... But, I have my own examples of this happening, so I know it can be done. And not through a hero's effort. At least, as far as man hours go... The initial thought process might be a hero's effort. Someone has to figure out how to build a steam shovel and realize that it can be applied to moving dirt, but once that work is done, it is "easy" for the driver to go through the physical process.

What is needed to make this happen is a toolkit that frees the dev to work the model and the code in parallel. An OO analogy might be a UML/Code integration package that did 100% seemless roundtripping. Picture making radical structural changes in an OO language via a UML GUI with 100% certainty that it didn't break the code. Picture being able to write the code, reverse engineer the UML, adjust it when the UML "picture" isn't correct and have the resultant code compile. Another example might be realizing that a hierarchy should really be a delegate, an swapping it out with a line or two of code. Or, realizing that switching to multiple inheritence might be a better abstraction mechanism for you problem space... (try to do that in your average language!) Pre-processor macros in a sufficiently advanced language can solve 80% of these problems in a lightweight way. But - they have to be easier to use than not use inorder for it to be effective.

I think the industry is heading towards this toolkit. Someone said that most languages are just a half-baked, partial implementation of Lisp, and that might be true. So, given enough time to catch up, most languages will become a Lisp? I have said that if Lisp just had CPAN, then there would be few excuses... But, in general, the sentiment is probably right on. There are a handful of features that really make that language powerful, and if they were just available to all languages, then it would make the ceiling higher. Of course, people argue the challenge to beginners, and that is, in fact probably one of the biggest gaps. But - then, you wouldn't expect a beginner chem major to start of splitting DNA, so I don't know why we expect beginning comp scis to understand everything right away. That is really the point of an advanced concept. The trick is to NOT take it off the table completely... Rather, let it be available when it is needed. If a language doesn't do macros, or expose the symbol tree in code, or have regular syntax, then the ceiling comes down.

I also tend to agree that the IDE is currently a bandaid and that will need to change for the toolkit to come about. The IDE should be the last in a series of steps. I think Unix has it more or less right, where the tools are written in libraries available at the command line and the last thing someone does is write a GUI around it. I think Unix has it wrong where no one spends any time on the GUI :). But Windows has it backwards, where the GUI is the first thing that a beginning programmer learns/writes about. There needs to be a healthy balance. And there needs to be more standardization in GUIs. Code Generation too... There are a bunch of efforts in this area to make more intuitive GUIS that convey more information faster. But all of that work needs to come after the robust libraries exist. The thing I point to first and foremost with GUIs is the lack of automatability. Which - probably your average user doesn't need (yet). But - if there is a bunch of functionality locked up in the GUI (which seems to always be the case) and you want to integrate that with something else, then you need a way to get access to the code in an automated way. Since GUIs also tend to be closed (at least on Microsoft), the options are things like SendKeys and Mouse movements, which are a bear to make consistently work. But, as it relates to Steve's article, GUIs have very little to do with code compression. If anything it will limit it. The GUI cannot keep pace with the model in someone's head. If it did, it would just be rendering data points in visual space. Writing a GUI is mostly about choosing how the model becomes concrete. And once that GUI exists, it becomes the handcuffs that prevent change.

Perhaps this will change with AJAX (which looks more like data driven GUI rendering to me than the good 'ole servlet/applet model). Or maybe it will be XAML or some other Microsoft project... Maybe it will be a total transformation of the "desktop" into something more along the lines of Johnny Lee's Wiimote hacks. I don't know. But I probably feel stronger that Steve that the IDE is a crutch that programmers could do without. Well... Still need a text editor, so how about an extensible one... vi or emacs :)

Thursday, January 17, 2008

Hiveminder, meet TaskJugger

I signed up for a hiveminder account yesterday. The IM interface was one of the main things that sucked me in. I did some similar work in a personal timetracking system a few years back and wondered how the two versions compared. Very impressive tool. I can think of about 10 uses for it already.

So, it dawned on me, a task is just a name that you are going to do work against... The tool tracks task relationships/dependencies... If it spit out the raw data as a TaskJuggler script, then it could generate the gantt chart of what my day/week/month looks like and show me how to optimize my time.

I haven't suggested it yet... Although they do have a novel "progammer API"...

Monday, January 14, 2008

Focus on the Customer

In general, I don't like to put a stake in the ground until the very last second (because I am lazy :) ), but I want to make a distinction between a couple different development models I have seen. I'll call them Isolated developement and "client-facing" developement teams.

The characteristics of the two are:

  • A Client-facing dev talks directly to the clients, aids in the modeling and requirements gathering and wears several hats. These are usually smaller products on a shorter schedule.
  • An Isolated Dev is separated from the client by layers of architect, document specialists, qa teams, support, etc... These teams usually support larger products or larger install bases.


The thing that becomes obvious when working closing with many customers on many small projects, is how much they all have in common. The benefit to having a dev realize that (as opposed to a sales person) is that they can abstract the commalities across a single code base and treat each client as an implementation or an extension of the same code base. Depending on the business that you are in, there is really not that much variation. This is especially true if you can abstract to a sufficient degree.

To this end, the client developer has a marked advantage.

If the dev can also figure out how to produce at the same quality and quantity that a team does, then you have a recipe for success. The product that emerges from that process is worth a second look. This blog is about that product and how to build it cheap with GNU/Linux tools. The concepts are general enough, that they could be reworked in any other toolset out there.

Monday, January 07, 2008

SCM is Competitive Advantage

SCM stands for "Software Configuration Management" as far as I know. But, it has links with "Source Control" or "Version Control", both of which are poor names. It isn't really about Control at all. Manangement is much better nomenclature as it better represents what is going on when a user works with source code.

Perforce (and probably others, maybe svn) talk in their documentation that all SCM is about is how to automate the processes that devs do naturally when writing code. Make it easier to do with the tool than to do manually, and people will use it. Once they are using it, it makes it simple to collect usage stats (among other things) and increase automation. If the loop then closes, and the user can tweak their experience with the tool based on pass usage patterns, or something else, then SCM is just another one in the class of tools that this blog is about.

But let me back up... The processes are usually fairly simple:

  • Keep track of multiple code lines (version 1, 2, etc)
  • Branching/Integration automation
  • Change Control ("Change Management" is better)


A couple features that are fairly key and are available it better tools now:

  • Atomic Commits
  • Version Diffing
  • Merge/Commit as opposed to Exclusive Lock


These are the things that devs need to manage a new or change job quickly. Many tools have features in support of all of these items. A few do not... But, the thing that is important here is that the processes don't really have anything to do with the tool that they are implemented in. I raise the issue because 9 times out of 10, I see a shop pick a tool, and then model their processes around the tool and its best practices. It is a step up from no tool at all, but it misses the point. The authors of the tools probably didn't write for your business case, they wrote for theirs. If your process and theirs are the same, then using it the way they do will work. But, if there is a mismatch (feature impedence), then I see shops change their business process to best fit the tool. One of the most powerful things that any shop has control over is their own business process. In the case of software, that process can actually be a competitive advantage and to potentially throw that away because the tool documentation tells you to is a bad move. And - while I'm on it - SCM isn't the only tool that this happens with, but that is probably another topic altogether.

Here is an extreme example. I worked in an Microsoft shop for a while, a few of them actually. Most of those shops didn't have SCM at all. While I was there, there was usually a push to integrate VSS. (Don't know how many people have ever worked with VSS, but I heard rumored a few years back that it was no longer supported. About a year later, I heard that Microsoft was on perforce. If that is true, I take it to mean that Microsoft couldn't get their act together on one of the most important aspects of their own business.) The thing about VSS is that it was a "Code Repository" which is different from SCM. It existed to dump your code into for backups. I tried a few times to demonstrate why it was a bad idea to pay $500 for a seat when Windows came with a free "copy and paste" command that worked on files. I was usually met with one of two responses, 1) We need to zip our source before we save it (huh?) or 2) but C&P doesn't do versioning. Raising RCS, CVS, SVN was met with boos, I think mainly because it wasn't Microsoft, or it was hard for those devs to get their head around merge/commit processes... They thought it was easier to walk to someone's desk and ask them to commit, so they could check out, commit and then give it back to the first person. That was true of outsourcing projects too... It was easier to wait overnight while the dude in India/Pakistan checked in and resume the next morning. When compounded across a team of 10, distributed around the world, this was probably the largest offense to business process I have ever seen. But then, I am caffinated and can't affort 6-10 hours of wasted down time.

Another argument that I heard in favor of something like VSS over others was that it was easy to learn. More than once, I heard someone say "We can bring in a person with zero dev knowledge and have them working against the source in an hour". In fact, here it is on a chatroom from back in the day. At the time I think I understood why you might need these people to do dev, maybe they were support, or docs or something, but it doesn't make sense to me anymore. If someone cannot figure out how to grab a readonly copy of the code from any tool out there, then they probably don't have any business with it anyway.

But, I digress. There are a million more examples of how the tool drives the process, instead of the other way around. And it isn't just VSS, that tool just provides the most extreme cases :)

The real point is to figure out how a team delivers source and make the tool support that process. Each team has to do this (probably) and would be wise to experiment a bit before settling in on a tool.

So, here is what I have settled into for the following reasons:

  • 0.00001 FTE admin overhead
  • Simple to support N versions of the product at the same time
  • No question about where a change is entered (only 1 branch policy - ever)
  • Very declaritive model


An early branching strategy by default actually includes a late branching strategy. To see this, look at the late branching strategy... Work on "main" until product release. At that point, create a version branch off "main" where maintanence will happen. Bugfixes on a previous release are integrated across branches on a case by case basis. The policy of "main" might shift between dev branch and integration branch. Patches will probably be maintained with labels on the branch.


-- main ----------------------------->
| |
-- 0.1 --> -- 0.2 -->


Now look at the early branching strategy... Start with versioned "dev" and "release" branches with "release" branching off the "dev" branch. All dev and QA happens on the dev branch, and the release@HEAD is always the most current release. Where the label used to track the releases, and dev was done on the release branch, now the releases are tracked independently from the dev work that is done. And in practice, the late branch strategy usually requires a bugfix branch if the fix is of any size at all. That branch lasts for the duration of the fix and then dies. There is additional branch management here, and it basically enforces the Exclusive Lock paradigm, because it makes it easier for a dev to wait than to merge fixes across branches.


-- 0.x ----------------------------->
| |
-- 0.1_dev --> -- 0.2_dev -->
| |
-- 0.1_rel --> -- 0.2_rel -->


Probably the most important thing in this model is that the branches only ever have 1 policy. That makes it REALLY easy for a dev to know where a fix goes. It also makes it really simple to figure out the integration patterns. The thing that is more difficult is the additional integration step into the release branch. But, if it is automated, then it is only 1 additional step and it has the potential to take care of a bunch of overhead tasks that the tool may not provide already. Think labels, timestamps, doc builds/updates, etc.

Another interesting thing is that the infra that supports the codeline becomes easy to change. (I will get to builds in another article, but it should be a safe assumption that all software has at least some infra.) In the late branching strategy, the infra grew with the code on the "main" line. If there was a bugfix in the infra code, there might be confusion around how to move it from a version branch to the main branch, especially if there was a bunch of retooling done in a later release. The early branching strategy is similiar to a datawarehouse in that the code and the infra on a branch should always work - you can always go back in time on a single branch and have it work the way it did 3 months ago. The one branch-one policy makes it easy to have separation.

There is more to it, but it is probably enough to say at this point that I get a ton of additional functionality just by following the model. The cost of the model is the occasional extra integration required for a production release or a bugfix.

Now, this model is independent from the tool. And I already know that I have a couple of points where I want to do some "custom" stuff. For example, I know that during a integration to release (a "promotion"), I want to cut a label, and send out an email with all the changes pulled up into release notes. I know that I will need an automated a way to propagate bugfixes into later branches, and while I am at it, just run my test cases so I know if there is a branch conflict. I haven't found a tool that implements this functionality for me, and I am not sure I would anyway as it is probably closely tied to the build, so I already know that I have some scripting that will need doing.

But, this is a good mindset because I now know very specifically the criteria that I will need to eval my SCM tool against. The more automation is supports, the better fit it will be (of course it has to do my bullet list above too). Also, I am already in the frame of mind that my process dominates my tool, and not the otherway around. I may hit a ceiling with the tool - so it's extension capabilities are a top priority. I will now spend my day to day working in the tool the same way I ever do, but I have a couple of additional tools that map my model onto it.

This pattern is one of the key pieces to going really fast. It allows me to work without an SCM or a branch admin - the tools replace that function. The only admin work required is SCM tool upgrade/backup and the occasional troubleshooting. It also means that the different phases of the dev work, architect/dev/maintenance can all be done in separate well known locations with a migration path between them. For me it cuts out the Branch Admin, the Integration Nag and any Build Policing. It is, by default a Continuous Integration model. To step outside the CI model requires more work than to stay in it. And, the automation scripts are generally small; less than 100 line shell scripts each and I am not a master shell programmer.

The faster you can run and the fewer people you need to go that speed, the lower the overhead, the higher the revenue. SCM is Competitive Advantage.