Apr. 17th, 2018

jack: (Default)
I was hoping this was going to be a bit more technical, but there you go. The idea is to write inspired by whatever I'm doing at the moment.

This is not so much debugging as, when you're sure the problem is fairly simple but it still sucks you in for ages and you can't tell if you're getting it fixed, how to avoid being dispirited. Which is a problem I used to have a lot. What has helped?

1. Talking to other people! Partly for a second pair of eyes, partly to just ground yourself and get someone else to share the situation and agree you're doing the right thing.

2. Take regular breaks.

3. Work through the problem step-by-step. This should be appearing here. It isn't. Check each intermediate step.

4. Decide if (often), it's worth making the step-by-step tests ones that can be saved in the test suite against future failures, rather than ad hoc "step through in debugger".

5. If you can, maybe work on something else for a bit.

6. Be realistic if this is still the most important thing to do or if you should be doing something else.

7. Be realistic, do you know as much as anyone else about fixing this? If so, seek to keep your spirits up and bring in second pairs of eyes as necessary. If not, don't be shy, seek out advice on how to debug the problem.

8. Take heart. I've faced a LOT of problems that have seemed simple but intractable, and looking back with the benefit of hindsight a few were beyond me at the time, and many would have benefited from a fresh perspective, but none were actually black magic, there was always a sensible way to proceed, and I usually found one.
jack: (Default)
I got all five sciences working at a fairly regular clip, and an extendable copy-and-paste array of assemblers I can use to extend the set-up, and I've pulled back a bit -- I'd still like to launch a rocket, and maybe see how much throughput I can work up to, but I feel like I'm over a hump where I can see how it will go and I'm less hungry for it every minute :)

But it still brings to the fore a lot of software engineering instincts I've internalised but not always had proper names for.

Five whys

My research is slow. Why? I'm not producing one colour of science pack. Why? Not enough (say) electric engine parts. Why? They're not inputted automatically, apparently I kicked the assembly off by supplying a big batch up front and it ran out. Why? Because they need lubricant so I was manufacturing them near the chemical plant not the rest of the assemblers. Why? Running a pipe all the way there wasn't convenient. Why didn't I carry them on a conveyor belt? Also inconvenient, but
possible.

Automate it the second time

OK, that's about everything. That also touches another lesson. The *first* time I think this was reasonable -- I wanted to test all the manufacturing worked, I already had a great excess of electric engine parts in storage, just manually inputting them let me get everything else set up ok. But then at some point I topped it up again. That's the point where it would have been quicker to make a conveyor belt and fix it properly (I could have topped them up manually while I was building).

Hence my rule -- automate it the SECOND time you need to do it. If you do it the first time, you'll waste too much time automating things you may never need to do again, or that you don't have a good idea which bits are important. But once you've done it twice, you'll almost certainly need to do it again repeatedly, so investing in making it trivial to repeat is worth it.

Although factorio also teaches, there's a difference between "easy to do another 10 times" where a bit of manually joining up is a more sensible trade-off, and "easy to do another 100 times" when it needs to "just work" 90% of the time, and "easy to do another 1000 times" (e.g. any library inviting other people to use it) when in 99% of the time it needs to work without a lot of on-the-spot investment.

Make it extensible/stubs

When I manually put in some intermediate products, it's often worth putting in a short length of conveyor belt, so if I eventually automate it, I can just bring the assembled intermediates in on another belt and connect it to that one, I don't accidentally build too closely so there's no room to add the belt after.

In small scale, this is a stub, a function which you intend to fill in later. But in a larger sense, it's writing things in a way that you can add to them later -- not necessarily in an "official" way like re-using functions or inheriting from classes although that's good to do when you can, but just saying, if I do come and edit this code in future, how can I make it easy?

That means things like, even if you hardcode something, ideally put it in a variable so you can SEE what you've hard coded, and that if you ever change it, *some* of the work is done for you by just changing that one variable, even if you need to do things manually.

Larger scale issues

Factorio, with elements of sim-something and also of programming games like human resource factory, more than most games I've seen, teaches larger-scale programming instincts like laying out code for a larger project.

Lots of little things like, recognising which bits should be roughed out, which bits need to be planned in detail now, which bits are likely to be complicated and need a nice big empty space to play in, etc.

Recognising what is basically fine forever and what will need to be updated to do the same thing 10x or 100x faster, and which bits will work at that speed and which won't, and when it's fine to tie yourself to the simple implementation and when you should make the effort to avoid stuff.

Self-balancing is usually a trap

In factorio, my instinct was often, "if I build everything in the right ratio, everything will Just Work" and I'll have enough intermediates for all my products, and I don't need any logic controlling the system turning one thing off when there's too much etc etc.

But that is usually wrong for all but the simplest. Simple systems do work like this, which I like: all factorio machines stop when their output is full, so you don't need to prevent them jamming or exploding or anything (unlike real life! :)) so as long as each thing is used by the right thing, then it produces as much as it can, but when production rate exceeds the rate they're used by the next stage, they back up along a conveyor until the production machine stops.

But for anything at all complex, it doesn't work like that. Intermediate products are shared between different products. Sometimes the raw material inputs for a machine run out, and then it falls behind machines which were supposed to be producing at the same rate.

Sometimes you make a mistake and connect two adjacent belts, and you get coal in your copper plate belt or vice versa. The system needs to cope with that in some way that doesn't end up with you having to manually flush large parts of the factory (picking up everything wrong on the belt so the machines continue). It *will* have to recover from errors, so making it do so easily is a requirement, even if it wasn't obvious at first.

You need to recognise where it's better to make a little subsystem where everything makes the right amount for the next stage, and where it's better to produce lots of intermediate in one place that's available to anything that needs it -- but not so much that everything else is starved of resource.

I had to learn this balance in real programming as well.

Coordination costs

When you build a LARGE factory, you find stuff you didn't notice in a small factory taking up a lot of your time. That's partly because there's just more of it. But it's also because there's more different moving parts that need to work together. Software projects are the same: you need to recognise the transitions from "all in my head" to "need a tracking system" to "needs a committee" etc etc...

Active Recent Entries