Here are some words about general constraints in projects. # build vs buy When should the part be locally built versus when should the part be bought and shipped? Note that there are shipping time and shipping cost constraints. upper and lower bounds: - buy X when you think "I can't even get the materials to make X at that price" - make X when you cannot buy X at any price it may be helpful to modify your concept of X so that it can be purchased. conversely, it may be necessary to modify X so that it can be made with the tools you have access to. # prototyping Every prototype has some resource cost, although the cost per unit may go down because of economies of scale. Number of protoypes: Cost usually goes up with the number of required protypes or builds. Also, for each bug in the design that you don't catch, you will usually have to build another prototype to see if you can remove that bug. time per prototype: Each prototype takes a certain amount of time to build. As the time it takes per build increases, the total number of possible builds will decrease. # manual vs automatization The cost of automating a task is usually greater than manually doing a task. The one obvious time that automatization makes sense is when the cost of manually executing some task exceeds feasibility and only automatics can feasibly achieve the desired results. # fail fast So there's this well-known thing in quality engineering where getting bugs out earlier is easier, and this other well-known thing in programming where doing projects beginning-to-end gives you foresight about kinds of problems that might happen and makes the earlier designs bug-free and more efficient and such. The right way to think about how projects get completed is as a dependency graph. A useful heuristic here is "How would I prove this is impossible as quickly as possible?". You want to prove the total task will work even if the subtasks fail, and otherwise abandon it. Then you want to prove each subtask is impossible, and replace it appropriately and re-plan integration as quickly as possible (etc etc). It's not as big a deal to structure things perfectly if you have infinite resources and can parallelize everything, which is how the space shuttle and particle colliders are built. The big danger is doing the non-failfast steps first with one person. If one component has a major problem, that means one node is unexpectedly big. In practice, people replace that component with another component rather than delay, or engineer around it, or just accept the delay. But the overall delay is not due to delay along a specific path--it's due to multiple delays, some on every critical path.
20:45 < za3k> my made-up common wisdom (i swear this is a valid thing to do!) is that you shouldn't expect a single unexpected failure point where something takes longer than expected, because of statistics and critical paths.
20:46 < za3k> rather, there's some inflationary procedure that experts in the field already know
20:46 < za3k> and the combination of all the major subparts works pretty much in the expected way
20:46 < za3k> uh, you might look at what large building construction projects do if there's a lot of cross-discipline integration and it's impossible for any people to understand the whole thing.
20:47 < za3k> integration in general is something small organizations do *not* understand, and NASA does
# critical path Intuitively the critical path is a sequence of tasks such that, if any task in that sequence takes one minute longer, then the whole project will take one minute longer. So, the tasks on the critical path have no slack. # yak shaving "And the next thing you know, you're at the zoo, shaving a yak, all so you can wax your car." The minute you start walking down a path toward a yak shaving party, you should evaluate other possible paths and choose one of the shorter ones. The more distant you are from the original task, the more likely your yak shaving decisions or dependency graph is going to lead to weak links and wrong necessity calculations. Baking an apple pie from scratch is entirely possible as long as you make up reasonable bounds for what "from scratch" means; otherwise you'll end up investigating high energy particle physics for how to create new universes for your scratch-built apple pie which is not usually what people mean when they say "from scratch". Yak shaving is doing all the stuff you have to do before you can do the task you were assigned. These tasks may look unnecessary, but they are in fact necessary. The unnecessary tasks that resemble yak shaving are simply unnecessary and wrong. Instead of calling wrong necessity calculation links "yak shaving", call them junk dependencies or unnecessary dependencies. see also *Situation*: You have a push-down stack for all your goals. When you hit an obstacle, you push "remove the obstacle" onto the stack. Then, when the obstacle is cleared, you pop the stack, and you are back at your original problem. *Problem*: For some reason, the problems you push onto the stack keep getting bigger and bigger and bigger, dwarfing the original problem, each one dwarfing the one before it. Forces that seem to make this problem necessary or inevitable: * A stack is easier to understand and use than some more complicated goal structure. And, goals seem to be hierarchical. Stacks are good for traversing hierarchies. * The bigger problems, if solved, will make a number of other problems easier, not necessarily just the one you originally wanted to solve. They may have value in their own right. They are worth solving thoroughly. * The goal inflation is not infinite; it seems that the goals shrink down again to something reasonable after the stack gets to a depth of about seven or so. * If you're just having fun, you might have fun exploring all these different things. * But you've only got so much time, and you don't want to give up your original goal, nor do you want to delay it for years and years. You feel like you are working on the irrelevant.
 priority queue q = new priority queue
   sorted by "importance-units per complexity-unit"
   so greatest will be removed first;
 insert initial goal into q;
 while (q is not empty)
 { remove goal r from q;
   if r is not worth doing, break the while loop;
   if r is simple, do r, else
   { (r is complex)
     write a simple, bare-bones, end-to-end version of the solution to goal r
       which omits many features;
     insert all omitted features back into q;
   }
 }
 stop;
# transaction costs "The people pushing micropayments believe that the dollar cost of goods is the thing most responsible for deflecting readers from buying content, and that a reduction in price to micropayment levels will allow creators to begin charging for their work without deflecting readers. This strategy doesn't work, because the act of buying anything, even if the price is very small, creates what Nick Szabo calls mental transaction costs, the energy required to decide whether something is worth buying or not, regardless of price. The only business model that delivers money from sender to receiver with no mental transaction costs is theft, and in many ways, theft is the unspoken inspiration for micropayment systems. Like the salami slicing exploit in computer crime, micropayment believers imagine that such tiny amounts of money can be extracted from the user that they will not notice, while the overall volume will cause these payments to add up to something significant for the recipient. But of course the users do notice, because they are being asked to buy something. Mental transaction costs create a minimum level of inconvenience that cannot be removed simply by lowering the dollar cost of goods." * * See [The Mental Accounting Barrier to Micropayments (Nick Szabo)](http://szabo.best.vwh.net/micropayments.html). "Here I present briefly a theory of price granularity based on a subjectivist view of prices. The function of prices, from the point of view of a shopper, is to let the shopper map his personal resources (budget) to his personal values (unique and not directly observable). This mental process requires comparison of the purchase price of a good to its personal value. This entails a significant mental cost, which sets the most basic lower bounds on transaction costs. For example, comparing the personal value of a large, diverse set of low-priced goods might require a mental expenditure greater than the prices of those goods (where mental expenditure may be measurable as the opportunity costs of not engaging in mental labor for wages, or of not shopping for a fewer number of more comparable goods with lower mental accounting costs). In this case it makes sense to put the goods together into bundles with a higher price and an initutive synergy, until the mental accounting costs of shoppers are sufficiently low. These mental accounting costs, not the physical or computational or amortized R&D costs of payment or billing method, set the main lower bound on price granularity. Judging from pricing granularity trends such as the trend towards flat rates in online services, online pricing granularity is far above suggested micropayment levels of a few cents or even fractions of a cent. The mental accounting costs for a typical on-line consumer seem to be somewhat higher than those in more familiar areas of commerce. # Irrevocable decisions * design so as to postpone irrevocable decisions until the last moment * minimize the number of irrevocable deadly decisions # Reconsidering assumptions about the shape of possible solutions Reconsideration is a useful method because many projects are technically impossible except when various solution-related constraints are relaxed. Perhaps the most famous example of this technique is the creation of bitcoin itself. Arguments from physics basically guarantee that decentralized distributed consensus is impossible, but once some of the requirements are relaxed it turns out that something like bitcoin is actually possible. Another good example is flight, where mimicing the form and structure of bird flapping wasn't the goal but rather heavier-than-air travel was a more useful requirement. After achieving flight, nobody was particularly upset that the method did not involve flapping. Likewise, when people talk about immortality they often mean "living forever with the body of a 20-year old greek god", but meanwhile there's an entire space of possible solutions that are easier to achieve if you relax the constraints on the types of solutions that truly meet the criteria in any productive or useful way. Another entertaining example is "artificial intelligence": while many think of thoughtful machines out-smarting humans, really a profoundly mentally challenged "sentient" pile of software would be a huge advancement from current attempts. # Failure modes mean time between failures (MTBF) Your theoretical best SLA (absent mitigations) is the product of your dependencies' SLAs, e.g. 0.999 * 0.999 = 0.998. But in the world of microservices, this logic seems likely to make you underestimate your uptime. You can build a service with a higher SLA than one of its dependencies, but only if you recognize that impedance mismatch and build in defenses. Build reliable services out of unreliable dependencies. Some services might be running on individual machines which each individually have relatively high failure rates. The same basic principles can be applied at every layer of the stack: make a bunch of copies, and make sure their failure modes are uncorrelated.