Þ   briarpig  » code  » goals


14aug09 « purpose

     This page expands a goals section on the top level code page.

deliverables «

     What you plan to deliver affects how you design and code—a lot. So engineers typically talk about goals early and often, in order to constrain scope of ideas to directly and immediately applicable areas. Why is that?

     At least two major reasons come to mind. First, by specifying what you plan to do as context, you can often drastically simplify a design to do only exactly what's needed and no more than that. What needs to happen can be wonderfully narrowing, so it seems a waste to even brainstorm without imposing end goal constraints first.

     A second reason will occur to most professional engineers: to stop you from running off in the weeds. Programmers are famous for getting wrapped up in useless details. Veering off the path of useful thought is a constant risk, so proof your line of thought is relevant to project goals is in continuous demand. Folks ask: Why are you thinking about that?

     However, a top level goal is missing in most of my code examples—my deliverable is missing, and this immediately suggests my examples are baseless without a shipping product context to provide a validating use case. However, a large number of products have basic performance requirements, and this becomes a sub-goal in many parts of low level code; it seldom varies much from product to product.

     If you like, think of my deliverable as just this: moving bytes around efficiently. Computing systems use memory and cycles to simulate a high-level, app-specific machine on lower level hardware provided by CPUs, switches, and storage devices. Your app uses at least one CPU and some memory, right? Okay, then my topics are relevant, because mainly I talk about using those effectively in rote habits.

plumbing «

     Most code I show is basic plumbing common to many kinds of app, including most servers at a very low level, no matter what your server does. Maybe your app is a desktop app, and you're not used to thinking of it as a server, even though it serves user requests.

     The plumbing in most apps is a bit messed up, at least a little. Every app and server I know does some feature in a awkward way, because a codebase grew so some things are funky. If for no other reason, this can happen from feature creep. When a product is first conceived, a set of planned deliverables are used to constrain code design, so code is written no more generally than necessary. But when you later add more features, they can fight with earlier decisions. Eventually, if you have technical debt from resulting chaos, you get a product organized internally by spinning an arrow in a Twister game. (Now: put a left hand on a red circle, but you can't.)

     I often straighten out difficult plumbing. I pull simple tools from a bag of tricks I started coding in the early 90's. These simple tools are what I write about. You might be able to use them to fix some of your plumbing. But inspiration is up to you: I won't say how to be inspired.

contiguity «

     Many plumbing problems are caused by an assumption of contiguity: that either memory or execution cycles are located together in an immediately adjacent form that's easy and convenient to understand—until contiguous memory and cycles are not available. A developer typically cobbles together quick and dirty solutions to lack of contiguous memory, when it happens, in a way that works in a specific context, but gets hairy when that context is missing when future features are added. An Nth generation plumbing solution can get byzantine in complexity, just from coping with discontiguous memory, copying contracts, and memory management.

     When cycles are not simple and contiguous, you get concurrent or async timing, or both. Concurrent and async code are especially hard, and I do these in day jobs (often wearing an expert hat), but I might write about them little on this site. It's too expensive for me to write about concurrent and async code, unless I'm being paid or I'm having fun. I plan to have fun writing about async styles in simulation engines, so that's on my docket.

     I'll write about memory use more, because it's trivially easy to discuss after many years of practice. My code examples may assume scatter-gather is the rule rather than the exception, so discontiguous memory is assumed from the start. Yes, this absolutely makes code more complex than necessary when you assume contiguous memory. But, this assumption is often wrong. For example, one often wants to divide server memory into re-usable chunks of uniform size in pools managed by flow control (meaning: you say no to requests, or delay handling them when you don't have enough memory to make progress).

     It's fairly easy to make one thing scatter-gather in isolation, using ad hoc technique. But when several interacting entities are all scatter-gather to address discontiguous memory, this is hell unless you have a clever plan, or an organizing discipline. The lazy, sloppy, slow way to solve this problem? Make new copies of everything each time. Ouch: this is a performance problem you need to solve. (I get tasked with stuff like this.)

     Coping with discontiguous memory is a consistent goal in code I show: how to operate effectively when memory is broken into discrete buffers holding parts of streams scattered hither and yon. If this isn't your goal too, you may hate my code.