Lead Bullets

Came across a great passage from Ben Horowitz' The Hard Thing about Hard Things.

In it, Microsoft had just announced IIS (Internet Information Server) which competed directly with Netscape's web servers, offering the same features and five times the speed ... for free. Ben faces a crisis and starts looking for ways to pivot the business and presents ideas to the veteran engineering lead Bill Turpin, who responds with:

Ben, those silver bullets that you and Mike are looking for are fine and good, but our web server is five times slower. There is no silver bullet that’s going to fix that. No, we are going to have to use a lot of lead bullets.

This, I think, is a valuable little metaphor. 

All too often, in any context (trying to learn a new skill, trying to lose weight, trying to find that new product edge) people have this implicit quest for that magical silver bullet that somehow solves the problem. Problem is: it doesn't exist. Producing a valuable result is by definition difficult and by definition demands a long, hard grind.

You're going to have to use a lot of lead bullets.

Disney On Art

Love this video. In the production of a film, the artists must place their skills at the service of a common goal, modifying their pet ideas to build towards a consistent style.

But on a field trip on a holiday, they decide to render a tree and individually come up with wildly different ideas, exploring different aspects of the tree. Despite painting the same subject, the ideas (and thus paintings) that emerge are completely different. 



How Complex Systems Fail

Came across this paper by Richard I. Cook, MD recently. Yep, it's written by a medical doctor and has amazing parallels to software systems. Anyone involved with software, especially engineers, should give this paper a good read.

The main bullet points:

1) Complex systems are intrinsically hazardous systems.

2) Complex systems are heavily and successfully defended against failure.

3) Catastrophe requires multiple failures – single point failures are not enough.

4) Complex systems contain changing mixtures of failures latent within them.

5) Complex systems run in degraded mode.

6) Catastrophe is always just around the corner.

7) Post-accident attribution accident to a ‘root cause’ is fundamentally wrong.

8) Hindsight biases post-accident assessments of human performance.

9) Human operators have dual roles: as producers & as defenders against failure.

10) All practitioner actions are gambles.

11) Actions at the sharp end resolve all ambiguity.

12) Human practitioners are the adaptable element of complex systems.

13) Human expertise in complex systems is constantly changing

14) Change introduces new forms of failure.

15) Views of ‘cause’ limit the effectiveness of defenses against future events. 

16) Safety is a characteristic of systems and not of their components

17) People continuously create safety. 

18) Failure free operations require experience with failure.

Raspberry Pi: How to Run a Program on Boot

Arduino has the advantage of being super simple and dumb and just running one program at boot. If you want your rPi to behave similarly, like an appliance (plug it in and go) then you'll need to do this:

sudo vi /etc/rc.local

Assuming your program/script is called MYSCRIPT in this example, all you gotta do is...
Edit the /etc/rc.local script, adding an invocation of your script  before the "exit 0"

printf "Running MYSCRIPT now!"
sudo /home/pi/MYSCRIPT &

exit 0

(Use the ampersand to background the process if your program is blocking for a while -- e.g. a server)

Bruce Lee was a Programmer

I stumbled across a pile of Bruce Lee quotes today; and it seems to me given the spirit of these one-liners that he must've been a deeply experienced programmer. In spirit. Maybe. Anyway, here they are:

Adapt what is useful, reject what is useless, and add what is specifically your own.

Translation: Don't reinvent the wheel. Import that fuckin' library. 

All fixed set patterns are incapable of adaptability or pliability. The truth is outside of all fixed patterns.

Translation: There's no silver bullet. There are no absolute design patterns. Every abstraction has its exceptions.

If you spend too much time thinking about a thing, you’ll never get it done.

Translation: Just SHIP IT. Don't over-engineer or overthink the damn thing.

It is not a daily increase, but a daily decrease. Hack away at the inessentials.

Translation: Delete code. Refactor, refactor, refactor. Less code is better code. Simplify that feature. Do they really need that checkbox?

The great mistake is to anticipate the outcome of the engagement; you ought not to be thinking of whether it ends in victory or defeat. Let nature take its course, and your tools will strike at the right moment.

Translation: Just try and see. Don't wring your hands forever over that new product or feature. 

Be like water making its way through cracks. Do not be assertive, but adjust to the object, and you shall find a way around or through it. If nothing within you stays rigid, outward things will disclose themselves.

Empty your mind, be formless. Shapeless, like water. If you put water into a cup, it becomes the cup. You put water into a bottle and it becomes the bottle. You put it in a teapot, it becomes the teapot. Now, water can flow or it can crash. Be water, my friend.

Not sure what to do with this one but I liked it.

Attributes of Beautiful Systems

Fred Brooks (author of The Mythical Man-Month, an awesome book on software development) wrote another, lesser-known (but also great) book called The Design of Design. It’s broader in scope, less focused on software architecture and more on the process of design. (BTW: the terms “design” & “designers” are used in a very broad sense here)

He does not prescribe any faddish low-level process (“scrum! flat design! agile! stand-ups! story points and post-it notes!”) but rather walks you through examples of the many mental foibles that designers may stumble over.

In pointing these out, he pushes you to be more reflective about your own habits and basic assumptions. For instance: consider the value and handling of brainstorming, which is typically just assumed to be valuable. Are the ideas produced actually better? (Actually, individuals outperform groups in the quality of ideas in their originality, feasibility, and effectiveness) Do the results tend to be better when you remove criticism from the conversation? (You sure do get a greater quantity of ideas and can reach much wilder solutions than if you allowed the naysayers to shoot ideas down prematurely)

My favorite chapter, I have to say, is the one entitled “Esthetics and Style in Technical Design”. In it, Brooks explores that fuzzy idea of what makes for a “beautiful system”. People can talk about beauty for cars, airplanes, houses, sculpture -- where the object is physical and has visual beauty. But people judge totally abstract things as being beautiful too: a theorem, a programming language, even a “visually ugly” but “logically beautiful” system such as Minecraft. Visual beauty is pretty apparent; so you might wonder, what does it really mean to be bear “logical beauty”?

Brooks lays out the prime attributes that define a logically beautiful system. I’ll list them briefly here:

 

Parsimony

The property of being able to accomplish a great deal with few elements. This is positive only up to a point, so beware of following this to the extreme: excessive parsimony leads to the use of non-obvious idioms. You can design a language with very few elements that’s completely insane to actually code in. You can have an API that only allows incrementation (but with negative numbers): parsimonious, yes; intuitive, no.

Brainfuck Language, which consists of just punctuation.

Brainfuck Language, which consists of just punctuation.

Conway's Game of Life. Very few rules, and technically Turing-complete.

Conway's Game of Life. Very few rules, and technically Turing-complete.

Structural Clarity

There must be a direct path from what you want to say to how you say it. The basic structural concept is plainly evident and if not obvious, then easily explained. Familiar and simple metaphors aid in this (e.g. the "Desktop" of Macintosh, the "spreadsheet" of VisiCalc, the "blocks" of Squarespace).

Consistency

Given familiarity with just part of a system, you can predict the rest of it. You'd expect all functions in a package to throw similarly formatted exceptions, use similar error codes, take similar input types. If the $increment function takes a path and number, so should the $decrement

Modern militaries are designed for complete uniformity. This trait evolved as a requirement to achieve scale and usability for commanders.

Modern militaries are designed for complete uniformity. This trait evolved as a requirement to achieve scale and usability for commanders.

Orthogonality

Do not link what is independent. A change in one function does not affect another. In other words: no side effects. Air conditioning in your car shouldn’t affect the lighting (though in reality, at a certain point all these systems draw from the same power supply and in extreme situations, orthogonality breaks down)

A system deliberately designed for unexpected side effects.

A system deliberately designed for unexpected side effects.

Propriety

Do not introduce what is immaterial. Do not expose incidental complexity, where extraneous implementation details affect the interface. Shifting of gears in a car is completely incidental and do not belong in its interface. An example of an egregious violation of propriety: iPhones being unable to use Facetime unless connected to WiFi. This limitation is completely arbitrary from grandma’s perspective.

Generality

Do not restrict what is inherent. Generality is the ability to use a function for many ends. Designs should not make too many assumptions about the use of a function. This is where Unix really nails it, keeping standard i/o, pipes, and files completely general.

Every block can connect to any other block.

Every block can connect to any other block.

... Enabling amazing constructions.

... Enabling amazing constructions.



The Black Triangle

A great story I remembered reading once on someone's blog. The link is now broken but I found the story copied on Wayback machine:

The Black Triangle
Reposting of a .plan I released a little over a year ago at GarageGames: 

In October of 1994, I’d just started as an honest-to-goodness videogame programmer at a small startup called SingleTrac – which later went on to fame and glory (but unfortunately not much in the way of fortune) with such titles as Warhawk, the Twisted Metal series, and the Jet Moto series. But at the time, the company was less than 20 employees in size and had only been officially in business for about a month. It was sometime in my first week – possibly my first or second day. In the main engineering room, there was a whoop and cry of success. 

Our company financial controller and acting HR lady, Jen, came in to see what incredible things the engineers and artists had come up with. Everyone was staring at a television set hooked up to a development box for the Sony Playstation. There, on the screen, against a single-color background, was a black triangle. 

“It’s a black triangle,” she said in an amused but sarcastic voice. One of the engine programmers tried to explain, but she shook her head and went back to her office. I could almost hear her thoughts… “We’ve got ten months to deliver two games to Sony, and they are cheering over a black triangle? THAT took them nearly a month to develop?” 

What she later came to realize (and explain to others) was that the black triangle was a pioneer. It wasn’t just that we’d managed to get a triangle onto the screen. That could be done in about a day. It was the journey the triangle had taken to get up on the screen. It had passed through our new modeling tools, through two different intermediate converter programs, had been loaded up as a complete database, and been rendered through a fairly complex scene hierarchy, fully textured and lit (though there were no lights, so the triangle came out looking black). The black triangle demonstrated that the foundation was finally complete – the core of a fairly complex system was completed, and we were now ready to put it to work doing cool stuff. By the end of the day, we had complete models on the screen, manipulating them with the controllers. Within a week, we had an environment to move the model through. 

Afterwards, we came to refer to certain types of accomplishments as “black triangles.” These are important accomplishments that take a lot of effort to achieve, but upon completion you don’t have much to show for it – only that more work can now proceed. It takes someone who really knows the guts of what you are doing to appreciate a black triangle. 

When I was working on Void War's principle development, I remember chatting with another SingleTrac alumni, and getting very excited about my multiplayer code. I’d spent a little over a week on the underlying architecture, trying to make it clean and reliable and easy to use. It’s all UDP rather than TCP/IP (for speed), so I created my own “guaranteed delivery” protocol for those rare packets that need to be guaranteed. I’ve rarely worked with UDP before, so it was kind of a new experience. When all was said and done, I had another computer join the game – and boom. There it was, on the host machine. Wow. No updates, it did next to nothing, but the core architecture was there. The rest SHOULD come together quickly. Explaining this to my buddy over messenger, I explained, “It’s a black triangle.” He understood what I meant immediately. It’s a convenient shorthand metaphor.So feel free to steal the term. And when progress seems a little slow because you are doing a bunch of hardcore architecture work, just remember – it’s a black triangle. 

Labels: programmingretro


- posted by The Rampant Coyote @ 1:09 PM (Permalink) 

Dynamic Typing

Dynamic Typing is like stuffing all your data into a pile of <String, Object>HashMaps and calling it a day.

It's fucking insane.

  • You lost all compile-time checks.
  • You will have to implement a million unit tests where a type system would have made these guarantees.
  • You literally have to find a way to exercise every single code path in your system in order to avoid the simplest of type mistakes. In this sense, you are forced to write exhaustive tests.
  • It's impossible, just from looking at a function, to know with any certainty what values are provided or generated.
  • Given a variable, it's impossible to know the valid range of values it may contain. 

The Trick to Solving Hard Problems Faster

Writing is nature’s way of letting you know how sloppy your thinking is.
— Richard Guindon

When I first heard this quote I thought gee, that's really true. In my experience it happens all the time; I have some foggy notion of something and when I start to write it down I start to see the gaps in my initial logic and assumptions, paving the way for a much better idea. Eventually I can evolve it into a much stronger piece. 

It's a great habit to just write things down, as they say. When I think about why this bit of writerly wisdom seems to be true, it jumped out at me that this is rooted in a basic physical fact:

Creating a good idea is NP-Hard.

This basic bit of Computer Science theory means that solving real problems is hard (generally believed to take exponential time) but verifying their solutions is easy (doable in polynomial time).

Mozart composing a great symphony is immensely hard, but appreciating can be done by anybody. Generating a mathematical proof to a tough theorem is hard and takes years, but verifying the theorem can be done by any trained mathematician. The pattern of examples of our society enjoying this relationship between creator and consumer can be found in every creative endeavor. (Off-topic: part of why education is so incredibly important is because it vaccinates everyone against outright lies...)

So when you finally write something down, you make the solution available to your own verifier. You've probably already vaguely known this intuitively (that you tend to sniff out a shitty proposal much faster than you formulate a good one) but without knowing why it worked or without much discipline. So now I urge you to remember and leverage this fact regularly: 

Next time you struggle with any problem, it's probably because you're thinking too hard. It's really hard to judge the million half-formed ideas when they're in your head, but when you can finally read them, the flaws are obvious and only then can you improve on them. Just write! Draw! Make 20 prototypes! Do anything to get it into a medium that you can lay your eyes on!

Alan Kay on putting yourself into the future

Alan Kay gave a great talk called "Normal Considered Harmful"

At 51:35 there's quite a gem. He advises us:

Don't be in the past. You need to put yourself into the future in order to skate to where the puck is going to be. You need to leverage up!

The way to get leverage is to get a ton of computer power and write a ton of very simple, non-optimized languages that run very very quickly. And you should be able to do them every couple of days to get power and then throw them away, use them like kleenexes.

Its rather easy to do that if you don't have to optimize and you don't have to optimize if you use hardware that runs 100x faster than what you have today.

So unless you do that, you can ONLY do incremental stuff today.

Another great point at 59:00

The MacCready Effect. 

Always do the minimal thing you can do that is qualitatively better. Before building anything, MacCready spent a long time thinking about where to spend 80% of his effort beore he did a darn thing. That's why he solved an immense number of problems in his life.