Unfortunately I found that foreign keys in my Rails migrations were a constant source of headaches. Moreover, I decided to migrate to Heroku during the project and had to re-write the foreign keys for Postgres instead of MySQL. What did I learn about customers in this process? Nothing.
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.
The initial success of MySQL is evidence against your assertion.
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.
When MySQL came out it [...] said that speed of development and operation matters more than correctness. [...I]ts initial success stands as a testament to the principle that there are a lot of problems (particularly in the web area) where people [...] are tolerant of occasional problems.
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.
What really baffled me about that anecdote is that I wonder what he was "learning about his customers" by migrating hosting providers and database systems. And yet, somehow, foreign keys get all the blame.
Yep. Sure, to survive in the long term, you've got to survive in the short term first. But taking a shortcut that ultimately results in losing a customer's data (or indeed, losing track of their money or their stuff) is a bigger risk than getting your product out of the door a day late(r than you wanted).
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 are computing scientists or software engineers who think before they write code because if you don't understand the process or the concepts or the abstractions involved, your code is going to be shit.
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?
You seem to be missing the point of the article: producing less bugs and better code can be wrong if writing that code slows down finding out what the problem is. Speed-of-writing-good-code is an orthogonal parameter, here.
No, it's not orthogonal. I can see it in things like the complaints about how foreign keys "caused problems" in the migrations. Generally, that means one of two things: The foreign keys were actually preventing bugs in your migrations (which even in a startup context is a Good Thing(TM), as "buggy migration" is effectively slower than "correct migration" no matter how fast the buggy migration is), or the developer was not experienced enough with foreign-keyed databases to really know how to migrate this.
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.)
Let's see if you agree with this restatement of your post:
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.
Yes. That's actually an important point that I only alluded to. There's a crossover period during which a hackhackhack coder will indeed have "something" out the door faster than me. If your startup fits entirely into that horizon, then, well, don't hire me. Or make it very clear to me that that's what the situation is. Some "startups" do fit into that time period, though as my scare quotes indicate I'm not sure that's really the right word for such an endeavor.
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.)
Two vectors are orthogonal if the addition of one of them does not affect the "amount" of the other, when using them as basis vectors. In this case, "(1,0)" and "(0,1)" are orthogonal; no matter how far in X you go, the amount of Y-ness doesn't change. (1,1) and (1,-1) are also orthogonal. In the metrics most people learn, this means the vectors are at right angles. (Forgive me this simplification, mathematicians, going further will only obscure things.)
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.
In simple English, orthogonal means "at right angles". People often say that two things are orthogonal when they mean is that they are different dimensions that are unconnected with each other.
To pick a silly example, the number of coffee cups I have is orthogonal to the number of jars of jelly beans I have.
I agree this mistake is typical, but I would not be surprised to see a senior make it.
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).
According to my definitions, good code must perform correctly or else fail gracefully. It must also be robust, debuggable, and maintainable. Those extra conditions mean thinking more, and writing more code. That takes extra time over hacking out the quick-and-dirty version.
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.
Yeah, an experienced startup developer can acknowledge and articulate the tradeoffs between time to market and the perfect design, sometimes delaying some of the design (e.g. perhaps deferring scaling tasks until it's needed) to get things done faster, but at a minimum, making it functional and maintainable.
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.