Hacker News new | ask | show | jobs
by jlokier 1115 days ago
I was asked to estimate how much time remained to complete all the components for a 3d game engine to make it perform excellently according to the design in my head. This was back in the days when you rendered every pixel on the CPU using every clever technique you could think of for performance, and you also needed similar geometry calculations as you do nowadays for higher level modelling and physics.

All the components, math, data structures, algorithms and APIs fitted neatly together but the big leaps in performance would only come when they were mostly done. One of those things where it's fast when all the parts are in harmony and slow otherwise. But hard to explain convincingly to others (especially those lacking algorithm or low-level performance experience) without a working demo. Even the APIs for other developers using the engine made little sense to them, until built, and then they liked them a lot. Sometimes you've just got to show rather than tell.

So I sat down and estimated systematically, going through each little item on the list as I imagined doing them: X hours here, X days there. Just wrote down everything that came to mind, and estimated each one independently. Let's just grind through this estimate one item at a time. I didn't pad it or try to make it fit any overall expectation. I just did the honest exercise of writing down each item to build and each X that seemed to fit that item.

To my surprise that was an intensive exercise that 2 weeks full time, just to write down all the moving parts in a list, which, somehow, all fitted together as a "simple" design in my head. It never seemed so large that it could possibly take that long to write down a list of one-liners. No wonder I had trouble explaining the whole scheme.

Was that a fantasy? Turns out it was accurate.

The sum of those independently estimated tiny parts came to about 2 years. Ouch! A lot longer than the design seemed in my head, which seemed closer to a few months, but somehow always slowed by life experiences, tired weeks, distractions from other projects' requests and trade show demos. Because of all those "unpredictable" factors I didn't believe my estimate was really going to be accurate, and figured that I must just be overestimating, adding too much padding, or not combining overlapping parts of items enough. I wasn't very exprienced with large projects!

And then I went ahead and worked on the game engine until it was working and performing well. I buried and ignored that document and just got on with each thing that made sense to me to work on as I saw fit.

The real surprise for me, the lesson which has stuck with me ever since is, that estimate and plan proved to be reasonably accurate even though I didn't believe it, didn't think hard about it when I wrote the items one after another, and completely ignored the document after writing it.

It took 2 years to get the components working really well together as my design intended, and by then just about everything on the list was done. I was unhappy it had taken so long, but had to admit that all the small items really did end up adding up as I'd written down earlier, written down, and the design really did work and perform well when the parts came together.

The list of things to build proved reasonably accurate, despite surprises on the way such as complete changes of target platform and new hardware coming out.

I learned so many lessons from that exercise and seeing it play out. One of them is, sometimes an honest but low level "add up the parts" estimate can turn out surprisingly accurate but is a huge grind to produce. Another is, perhaps I'm not as bad at estimating as it seems when I'm asked to throw out an estimate quickly without giving it that kind of detailed examination. So now, I'll consider doing an exercise like that, if there's time, and if I don't like the answer, I should treat it as a harsh fact rather than something malleable by wishing.

Another is, like many programmers, I'll sometimes imagine a design that seems simple to me on the face of it, imagined almost magically fast visually, yet it turns out to have a huge number of details when examined properly that take weeks just to list and years to implement, yet marvellously something so complex does end up working pretty much as conceived in the first place, including big leaps of performance, good features and useful APIs. That ability to conceive of something in a flash that actually works well yet is vastly more complex than it seems on the surface is a kind of superpower, if you're looking to push the state of the art or solve stuck problems, but it's also a problem for what are hopefully obvious reasons. I have to be careful to not answer questions like "can we do X" with "[thinks then] sure, just do Y and Z this way and it will work" because it never sounds like years of work when summarised, but it can be. And I have to be really careful with side projects, where simple ideas correspond to months of work and a few favourite ideas corresponds to centuries as I zoom in, like a macro that contains much more when expanded then you'd expect. Life is literally too short to implement most of the things I think of that I'd like to build, and I have a disappointed feeling that some of them would actually work, but I'll never be able to implement them to find out for sure.

Another lesson from that particular project was: I was asked if I could add a significant capability to the engine: change it from quasi-3d a bit like Wolfenstein/Doom to full 3d like Descent but including surface physics. Having just done that estimate I was loathe to rip up a perfectly good plan that already seemed daunting, and add a huge, onerous change, breaking countless invariants and assumptions. So I said no. Years later after leaving the job I looked back with new insight and realised I should have said yes, taken two weeks of low-pressure vacation to let my imagination conceive how to make the more versatile thing work (at every level, from rendering up to physics), and I would have ended up with a simpler design than the original that would have shipped more quickly. The more powerful, general solution was much simpler to reason about, just as performant, and had more value to the business long and short term, as well as being more satisfying to use.

All I needed to do was rip up some working assumptions and take a look with fresh eyes, to get something simpler and more powerful at the same time. So now, if I asked, I remember that "no" as a mistake. I think it's often best to evaluate "go bold" changes with an open mind.