How Experienced Developers Can Handicap a Lean Startup
kevindewalt.com
kevindewalt.com
I think real experience and talent as a programmer shines in the ability to find a solution which is both correct and takes the same amount of time to write as garbage. This is not always possible, of course, but I would caution against coding without thinking about this trade-off.
In the case of the original article, the author cites the foreign key debate. Truth is, because reasonable and good programmers disagree on which is better, this is not even a good example of quick-and-dirty versus the-right-thing. If anything, it's an example of difficulties in storing data in traditional relational databases.
While I agree with Mr. Dewalt's basic message, identifying integrity constraints as something a "lean startup" shouldn't worry about for a production application containing his customers data undermines his implicit claim of data management competency - experienced database developers know how to design and organize their schema so that details like dbms-specific foreign key implementation are trivial. When foreign keys cause you serious pain relative to your other data management problems it means you're doing something wrong.
What he didn't learn about his customers in the hour at most it should have taken him to get the keys right (thus helping ensure his customers data is not corrupted by application, framework or provider infrastructure bugs) pales in comparison to the lesson he will learn if their data is silently corrupted.
When MySQL came out it was the first widely used database that explicitly said that speed of development and operation matters more than correctness. Experienced DBAs were shocked and horrified. Over time the database has matured, however its initial success stands as a testament to the principle that there are a lot of problems (particularly in the web area) where people care that the site does what they want and is responsive, but are tolerant of occasional problems.
Yes, I am aware of all of the possible shortcomings. However requiring people with good ideas to understand more theory before they can get things done does not help motivated newcomers to get interesting things done. And lots of newcomers getting interesting things done leads to successful startups. Which is how a crappy but convenient database (MySQL) got traction in the first place.
For a classic essay on the tradeoff between convenience and correctness, see Worse is Better at http://www.jwz.org/doc/worse-is-better.html.
I wonder if this would still be true today? Things are very different from 1994, when MySQL was born.
Back then, the Web was so small it was almost possible to visit every site. Site designers, site developers, and customers were all trying new things constantly and learning what worked. There weren't many expectations because everything was so new.
Today, my guess is that people have a much higher standard for e-commerce. But that's just a guess.
One is learning to do the same thing over-and-over again, getting somewhat better at it. This often results in being more rigid.
The other is learning the purpose and strategy behind what you do and being able to alter your behavior accordingly. The often results in being more flexible.
An experienced developer probably has a combination of these two ingredients. Thus there's no guarantee that an old timer will be more flexible or less flexible than a beginner. It's more a question of whether a developer has kept their learning and understanding to be at a high level.
I mean, it's important to realize that the purpose of a full ACID database is to maintain the integrity of enterprise data - key data that is used and re-used by multiple departments. Of course, in a start-up, the ACID requirement can be modified depending on whatever decreased need there is for the properties of ACID. An experienced developer looking from a high level doesn't just recite ACID requirement or glibly toss them out the window but looks at the actual requirements and modifies things accordingly.
Good developers work fast and well. They produce less bugs and better code. That's just why you pay them more.
If your experienced developer is slower at doing better, he just doesn't have the proper amount of seniority.
Remember that TDD advocate who tried to write a Sudoku solver using TDD methods. He went up against Peter Norvig but that is irrelevant. Norvig knew how to think about the problem but the advocate did not. So the problem isn't how many lines of some random programming language that you can bang out and how quickly you can fix bugs in it, the problem is do you understand the problem and the solution you are creating?
Bear in mind that if you screw up hard enough there's no guarantee you can recover. Anybody hanging around this site for long enough has read at least a couple of stories about the technical error that brought the whole startup down because there weren't enough resources to recover, and "botched database upgrade" (combined with "failure to backup", which of course occurred because there was no time, we're going too fast and we're too awesome!!! to do the backup) is a prime candidate for that sort of event.
I say the latter because I've been there. Setting up foreign keys is one skill. Learning how to migrate them is another. I've screwed it up before. It does take a bit of learning. But once you learn it, you're still better off using them properly in the first place. There's also some skill in learning how to set up your database so it can be migrated; I'm not even sure how to begin putting it into words, but there's definitely some skill involved there. (I'm not guaranteeing I've got it all figured out, but I've certainly improved, which is enough to establish that it is a skill.)
They're not equivalent either, certainly, but when you say "orthogonal", you're making the claim that they are not just different (which is true), but utterly unrelated, and that claim is unsupportable. I've been deliberately spending my last couple of years learning how to build things both fast and right, and I've gotten to the point where I can spank anybody just "hacking around" after about a two-month window, after which it's all profit. In fact I'm building a system replacing just such an effort right now... well, after I stop posting this anyhow. My system isn't a glorious paragon either because as an experienced developer I know where to cut corners to get it out, but it's a lot stronger than the hack job it's replacing (and I'm doing it in less time, too).
(Part of the reason I started this learning is that I was at a startup where I did in fact make this exact error. In the end, this is not what killed it (what killed it was the management insisting that there was always just one more feature we needed before release, and then once we had that, there was just one more, repeat until out of money with no customers), but I've made this mistake and I won't make it again. But the alternative is not hackhackhackhackhack, the alternative is being more familiar with the cost/benefits ratios and picking the winners, some of which will still be "do it right", and some will not. Hardcoding the answer in either direction is wrong, as it pretty much always is.)
Fast, mediocre coders may be able to iterate really fast in order to get something out in front of users. This means that when you don't know what the problem or the solution is (as discussed in the article), they can be effective at identifying the problem that requires the solution.
But if their crummy code gets used as the nucleus of the solution (once the problem has been identified), the startup can often ensure its own doom.
This means that in successful startups, speed-of-writing-good-code and speed-of-problem-identification are strongly correlated, and therefore cannot be called orthogonal.
I agree with this & think it's a useful refinement to my thinking. Thanks.
However, I've gotten my crossover period down to about two months on most projects. Some projects even less; a hackhackhack coder who goes for PHP with no frameworks is going to have trouble keeping up with me working in Django. (In this case, I don't intend this as a criticism of PHP, as the sentence indicates.) Of course an experienced developer will reach for some appropriate framework in PHP, but the entire point is that we're comparing experienced v. inexperienced.
(Separately, I don't want to make a separate post for this point, but: There is a tautological element to my argument here, which is that if a putatively experienced developer makes a bad choice on the do-it-right/do-it-fast scale, then by that very fact they have shown they were not an experienced developer. There's a bit of "defining myself the win" there, but I think there's still a point to it. And like I said, I've made that error and am trying not to make it again.)
Programmers tend to abuse this term to mean "the two things are totally unrelated", even though the term really only applies to certain mathematical constructs like vectors. Claiming that type-safety is orthogonal to garbage collection would mean the two are entirely separate concerns and that you can freely have none, one, or both, and that the two issues never matter to each other. Now, technically this isn't quite true, which is why I say we "abuse" the term, because the full meaning of "orthogonal" is that you will literally never be considering the effect of one while examining the other, and this is not true; type systems do some impact on GC, for instance. However, in many cases this is "true enough" for someone to say they are "orthogonal".
In this case, I'm not arguing merely that the precise definition is not met, because it pretty much never is, but that the spirit in which programmers use the term is also not applicable here, as the questions of "code quality", "speed", and "developer experience" are indeed tightly interrelated and so answers like "you always have to sacrifice quality for speed" are too simplified.
To pick a silly example, the number of coffee cups I have is orthogonal to the number of jars of jelly beans I have.
Trying to be industrial strength for a proof of concept or an experiment is typically a junior's mistake.
A related mistake is, of course, to keep a pilot project in production (usually as the core of the production project, with a variety of both functional and structural additions to keep it sort of working). This mistake is usually a result of a management decision more than development (but could be the same person).
For example, right now I'm helping a friend out with a project to send email. In about 10 minutes I could whip something out that does this using backticks (i.e., shell escapes), no error checking, no logging, etc. It would be in front of the customer and suitable for feedback very quickly. It would also be short, easy to understand, and would do its job correctly 99% of the time. All of these are good things, but it would still be bad code, because the other 1% of the time it's going to break horribly and someone is going to have to come in and fix it.
It's like the army corp of engineers. They can make a bridge for the general public, but they also know how to design something quick (and hopefully sturdy) for troops and supplies during a conflict. It may not be pretty, but it gets the job done when it's needed most.
If you want software engineers to be recognized as actual engineers they have to conform to a code of ethics and other standards. When the employer says "let's just cut this corner", the engineer says "NO we cannot!" instead of trying to justify and rationalise it.
I dislike the term software engineer and I think computing science would be better and more applicable. A scientist doesn't shy away from his responsibilities and he sometimes tells the truths that hurt.
We already always ship buggy code. That part is done for you. What this article advocates is going further and questioning almost everything that can lead to a proper design.
Seems like an experienced developer would have written a data access layer that wasn't so concerned about how the underlying data store managed foreign keys. Almost all senior level developers know that foreign keys are a performance inhibitor and plans for eventual denormalization of data.
I'm wondering if this person is actually an experience developer or not. He starts out saying one of the problems he solved would be to “figure out a way to speed up the data processing”, yet he doesn't understand how foreign keys play into this and had to work around normalization?
I also wonder exactly why it took so long to rewrite the application because of foreign keys. I've never migrated from mysql to postgres but if the process was so time consuming to warrant a blog post like this, I really question the amount of experience this individual really has.
The alternative to FKs is to write your own "middleware" or "business rules engine" and guess what, that doesn't get you to market any quicker either.
I want to iterate on my game's level design abilities to choreograph various enemy AIs with detailed scripts - things like "this group will spawn over here and take this path over there, shooting at these intervals."
But figuring out what the best paradigm, the best data format and tooling to help me iterate on the choreography is - is a guess. I can make a bells + whistles solution, but it might not be the right one. But neither is it something that can just wait and becomes obvious at some later date - the technical solutions and the design solutions are intermingled and have to be resolved together. So even though I'm my own customer, I'm still in the "lean" stage of this feature.
So I'm trying to stick hard and fast to "stupidly simple" means of tackling each problem I encounter. Instead of making one all-powerful choreographer system I've started with the stupidest, simplest one I could think of, hedging my bet by allowing multiple systems to coexist, while making the key pieces of data I already know I need, like spawn positions and trigger targets, sit in a different layer where they're easy to edit and interchange.
Essentially, I want to ride on the "80% solution" for a while and then gradually accumulate the rest, and I think the key to making that work is to know where you are uncertain and start fencing off the most suspicious areas so that you can build the eventual shift into the architecture - not modularity so much as "this code will someday become legacy code, so treat as if it already is."
I'm really sleepy right now so this might seem more profound to me than it is.
Marketing: We need these features now to close $1 mill deal Dev: but... but... we can't implement them in 2 days... we need more time Marketing: Go figure out something...
Marketing: Remember that Y features we asked last month? well, sorry, the customers decided to go with X... Dev: Uh... Uh...
Perhaps once in a while we should switch side: hey management/marketing/sales/CEO, do whatever you need, without modifying our software, to close out deals and to grab huge market share... go figure out something...
(Won't it be a founder selling anyway? I reckon it's a tremendous advantage if your biz dev person/people know one end of Textmate from the other and can do prototype-building spadework, too. It's hard to really get how a job feels if you've never done it. Marketers gotta code and coders gotta market...)
Big, slow thinking sessions ignore this: in an early stage startup you don't know what you're selling. You don't know until you sell it. So you need to find out by showing people endless variations on your core theme, and an iota of effort put into making one which doesn't sell is wasted effort, and wasted effort is death.
You are going to kill nearly all your features. That's code which gets discarded, but it's served it's purpose - working out what the business really is.
Wouldn't it seem to be a mismatch from the very beginning?
http://blogrium.wordpress.com/2009/11/05/why-efficiency-is-n...
Hacking together a solution may be more efficient in the short term, but it may also distract you from discovering an effective solution. Diving into a solution before understanding the problem is often a recipe for wasted time. Regardless of whether your solution is a hack or a gem, you're committed to seeing it through. Take time to frame the problem before you commit to taking a stab at it.
Measure twice, cut once. Teak or balsa.
I would rephrase this to:
Thinking about solving problems TRW (the right way) almost always takes longer than thinking about quickly hacking together a solution.
Or graphically, with "t" as "minutes thinking" and "d" as "minutes doing":
The quick way:
ttttttdddddddddddddddddddddddddddddddddddddddddd
The right way:
ttttttttttttttttttttttttttttttdddddddd
If you want to do something right the first time, thinking about it almost always takes longer. But actually doing it can take less time, often much less time.When trying to convince my customers to do things the right way, I often tell them, "It's not how soon we get started, it's how soon we finish."
The article is saying that you often want to get through each of the following lines as fast as possible:
ttd (release)
tttdd (release different feature set)
td (release totally changed and simplified app)
tttttttttttttdddd (now we understand the problem, do a good job)ttttttttttttttttttttttttttttttdddddddd
Startups have areal risk of running out of money, just before they complete enough d. However with
ttd,
I have a shot at cashflow with that d, which I can invest back into more (nt +nd)
Established companies dont have that risk so they can do
ttttttttttttttdddddddddddd
While for startups it makes sense to do
ttddttddttddttddttddttdd... etc.
tddtddtddyou already invested a lot of other t's and d's into doing it the wrong way
But the first three steps were an incredibly valuable investment -- now you know the right problem to solve!
(Maybe you mean something like this, just not sure from your comment.)
I'm just talking about the technical approach. You've gained no new insight into what your business is supposed to be, you've just learned a hella wrong way of solving a technical problem. Now your thoughts are cluttered with the effort and you had to spend time unlearning the previous approach.
I fully agree that iterative analysis and development is usually quicker than the waterfall method. I'm just adding that a little thought each step of the process can make everything quicker. If you think things through enough (which more experienced people often do better), you can have your cake and eat it too.
I'm stealing this. :)
Cleaning up should be faster.
It's strange that thing A is optional but without doing thing A up front you can get yourself in a mess such hard to clean up. If thing A is optional cleaning up the mess resulting in not doing thing A and having bad luck should be much easier. Db guys tend to overlook interesting problem how to get out of the mess. They just have pie in the sky rules and if you don't use them all and get yourself into trouble then it's your fault so suffer!
Real experience teaches you how to choose the right balance.
You probably don't actually need a database at all , given that the integrity of data stored is not paramount. You don't get any points for "We have this data. We have no way of assuring it's correct or is what we want it to be - but we have it!"
On the other hand, given that most start-ups don't bother with things like a business plan (at least one which doesn't end with "Step 3. ????? Step 4. Get bought by Google"), perhaps it's only appropriate that such start-ups do away with other fundamentals of reality, such as data integrity.
Should you use small variable and function names because they are easier to type? Throw away all error-checking because without customers errors don't matter? Or as this article says, perhaps toss out FKs because they might be difficult to refactor?
Trying to make the argument that it's one way or the other is really taking a very complex decision and boiling it down to comic-book level. You need to do enough of The Right Thing in order to make it to your first value inflection point, not just whatever it takes to score a couple of dozen freebie users (although every user is awesome!) Without some idea of where you are in your startup process and what your next value point is, you can't make those kinds of decisions. They're weighted choices.
Look at it this way: assume that you hacked something together doing almost the "wrong" thing every time and ended up 2 months later with a couple of hundred freebie users. Perhaps a dozen or so are even paying for your code, say ten bucks a year. But guess what? Your income won't support the amount of maintenance you have to do on your sloppy code, and customer growth is inadequate to pay for anybody to help you out. Somehow the bills have to be paid, and instead of sales and growth you're spending a large portion of your time as "database garbage man"
So you're stuck with crappy code and not enough resources to maintain the customer base you currently have. It's called "getting upside down" in a project. Not a happy spot to be in. (And it can happen without the lean code problem)
The best scenario is where you make the conscious decision to do a few quality items even at the expense of customer growth for a few days or a week or two and then consciously make the decision not to do a bunch of other things because the hit is too much.
It's complicated.
As Greenspun said once, if we want software engineers to be treated with respect, we need to force employers to respect a certain minimum standard of quality.
Regarding your example...by releasing something badly designed/programmed into the wild and taking money from customers, you are committing fraud. It needs to be seen this way otherwise we will never be forced to take the time to create quality software.
Could somebody please defraud me like that? Please?
As long as the software can run, we're rewarded for creating it, no matter its quality.
In our own minds, we have to treat that as fraud and need to feel awful about it.
All the quality in the world isn't going to make a pointless program worth anything. But a crappily-written program that provides lots of value is a great thing no matter how horribly it was hacked together.