In groups of people, there exist formal structure and informal structure. Formal structures are documented and consciously decided upon. They’re visible and accessible to everyone in the group. Informal structures form spontaneously, as people form connections and friendships, and information is transferred through conversation. Not every group has a formal structure, while informal structure is inevitable.
There are benefits to each kind of structure. Formal structure is defined, so people who are new to the group can learn where to go to get things done. Formal structure is possibly reproducible. Most importantly, formal structure scales. On the other hand, informal structure is efficient. Communication happens spontaneously. There’s no overhead. If you’re part of the network, that informal structure works for you. If you’re not in on those conversations, you miss out.
Because informal structure forms naturally and organically, it appears in our code, too. Informal structure takes the form of coupling between implementations of modules, because they know too much about each other. When you use one-letter variable names, or mutable state that makes sense to you but isn’t fully documented, or fields named for historical rather than current purposes, then informal structure is happening. This can work on a small, stable team. It doesn’t scale, and it presents barriers to new team members. If you’re a consultant and you want your customer to be stuck paying you forever to maintain the same system, then informal structure is for you. If we prefer to move on to solve new problems, then we must fight our brain’s intrinsic tendency to rely on informal structure.
In code, formal structure comes from careful design. From structured programming to abstract data types to message passing in OO to functional purity, good design practices all work to impose formal structure on code. Dependency Inversion Principle says “the implementation and user of the library must interact only through this abstraction.” Interface Segregation Principle says “I will know as little about you as possible, so I can’t interact with other parts.” Single Responsibility Principle says, “My job is to find the supplier with the best prices, so I do not know how to play golf with vendors.”
Informal structure is great among an agile team, which is sized and cultured to make sure knowledge is shared with every member. It’s efficient. It lets us treat each other as whole persons, which helps us be fully present at work. It’s the way our brains work best.
Our code does not benefit from informal structure, especially at any scale bigger than a screenful. We diligently apply design principles and coding standards to impose formal structure on our code, so that our software can grow and scale and be useful long after we have moved on. Fortunately for us, that class that approves travel reports doesn’t mind if we forget to say “Happy Mother’s Day.”
The inevitability of informal structure: http://www.jofreeman.com/joreen/tyranny.htm
In science we talk about “survival of the fittest.” But really, in human evolution, it is survival of the “fit enough.” Once a particular characteristic is good enough to keep us alive, then selection of other things will be more significant. There are many factors at play, and which one matters in a particular group and time is about random circumstances.
Like, why are our teeth so much work, and why do our gums recede and our teeth rot as we age? I wish nature had spent more time selecting for long-lasting teeth and less for high testosterone.
As people, we have something nature doesn’t: imagination. We can imagine and build teeth of metal. Natural selection often stops at “fit enough,” and it is humans who strive for perfection.
In our work, we have choices. We can aim for “fit enough ” or we can take the time and the risk and shoot for perfection - or at least the “fittest” the world has yet to see.
In evolution the unit of selection is the gene. The time scale is centuries. These days that’s so slow it’s irrelevant. The modern unit of evolution is the idea, and ideas evolve on scales of weeks to decades.
Where better for ideas to evolve quickly than the software industry? Ideas are our medium and our subject matter. We have choices: we can aim for “fit enough” at the safe point in the technology curve, using Best Practices and languages that have been proven useful. Or, we can aim for “fittest” by taking risks on more advanced languages and practices.
Waterfall used to be “fit enough” for all business software, until the Agile people looking for something better hit on a new “fittest” solution. Now Waterfall is falling off, and people are aiming for a new “fit enough” that’s better than before.
Java brought memory management from the edge into the mainstream; now garbage collected languages are “fit enough,” and we’re looking for better concurrency support among the modern “fittest” contenders.
We can be safe and survive with what the industry has deemed Best Practices. If we want to excel, we need to aim higher. Let’s keep our teeth sharp and strong with imagination and constant growth. The goal is constantly moving, and so are we.
“Never tell people how to do things. Tell them what to do and they will surprise you with their ingenuity.” - George Patton— Michael (Doc) Norton (@DocOnDev) March 31, 2013
The same can be said of declarative programming. If we tell the implementation what to do, not how to do it, then the implementation is free to improvise and innovate. This might happen in a library, in a compiler, or at runtime. Focus on properties the interface advertises, and we aren’t limited to implementations we can understand.
In her classic article The Tyranny of Structurelessness, about the women’s liberation movement and every group people ever, Joreen describes the magic self-organized, effective team:
“working in this kind of group is a very heady experience; it is also rare and very hard to replicate.”
Why yes, yes it is. When companies manage to build a cohesive team, they should value it. Because companies aren’t made of business plans; they’re made of people. Great products come from teams like this:
1) ”It is task oriented. …The task determines what needs to be done and when it needs to be done.” (on a wall with post-its!)
2) It has a common language for interaction. The easy way to achieve this is homogeneity. If you also want diversity, then we need “everyone [to know] everyone else well enough to understand the nuances.”
3) There is a high degree of communication. Information flows between all members of the group. “This is only possible if the group is small and people practically live together for the most crucial phases of the task.”
4) “There is a low degree of skill specialization. Not everyone has to be able to do everything, but everything must be able to be done by more than one person.”
Sounds a lot like an agile team! These unstructured, close-knit and collectively-motivated teams are just what we’re aiming for. Joreen documented these back in 1970, and now software gives us the field to attempt to replicate this.
Hilary Mason writes about what she’s learned about travel: take half as much stuff as you think you need, and twice the money. In other words: prepare less and be resourceful more. This leads to flexibility and great surprises.
Someone on Svbtle wrote about design in the same way. When you approach the layout of a page with a standard template in your hand, you’re bringing what you think you need to make the page quickly. This limits you! It limits the page. This particular part of the site may not need a login link at the top, or a search bar. Perhaps something else would be better in that spot, or nothing.
A great web site is more than functional. It’s an experience. Optimizing user experience, like taking a trip, is best done with less baggage.
Let go of decisions you’ve already made and look afreash at today’s feature, today’s adventure.
I wish Svbtle had a search feature that I could find :-( I’d link that post if I could.
My martial arts instructor said yesterday, “You don’t approach an opponent thinking, ‘I’m going to use a jab hook cross!’ You get up there and you find the opening they give you.”
In programming, we shouldn’t approach a problem with a solution in mind. Study the problem, find its shape, and suit the solution to it. This can be tricky when we’re constantly learning, because whatever new move we’ve recently practiced will jump out at us as a good idea. As in martial arts, keeping the right distance from the problem and opening our minds to all modes of attack will bring success.
The UNIX Philosophy has nine principles. These same principles are core to functional programming as I understand it.
Below find the nine principles of UNIX interpreted in terms of FP, ordered by the strikingness of the parallel.
1. Small is beautiful.
Small functions, small abstractions. The simpler the function, the easier it is to see what it will do.
2. Make each program do one thing well.
Each function should have one purpose.
9. Make every program a filter.
Make it composable! It is composability that builds functions and abstractions into larger systems without complicatedness.
6. Use software leverage to your advantage.
Composability leads to reusable parts. Also, write tiny DSLs for your own use.
3. Build a prototype as soon as possible.
The interactive REPL lets us prototype functions.
4. Choose portability over efficiency.
Don’t depend on the environment, even if it would be easier to root around in the database or system properties. Keep functions “data in, data out.”
8. Avoid captive user interfaces.
Imperative style may be most obvious to the programmer, but it is not the most flexible for her long-term use. Avoid inflexible contexts that look easy-to-use.
5. Store data in flat text files.
Functions should have visibility into the data, and its structure should be clear. With immutable data, choose clarity over encapsulation.
7. Use shell scripts to increase leverage and portability.
OK, yeah, I got nothing.
principles drawn from: http://www.pappp.net/?p=969
Of toddlers and of workers:
Develop structures and systems that free. Establish boundaries, and let the children play.
Programming languages do this when they restrict mutation, or when they enforce types. What seems like a burden gives us freedom and confidence as we write.
When a tester reports a bug and we execute the steps to reproduce the problem, we usually need a copy of the data in the tester’s database to get the same behavior. We need the program to run in the same context. Sometimes, environment variables and operating system and VM parameters and log configuration must be reproduced in order to reproduce the bug. The context of the running program determines behavior.
Each of our brains is a context. Words and stories have different meanings to each of us, and they have different meanings at different times. Our context — the existing patterns in our brain, and which neurons are active at a given instant — changes continually.
One superpower of the human brain is simulation. We can evaluate possibly courses of action by simulating them in our heads: What would happen if…? These simulations include the emotions we expect to feel. When we remember an event, we put together pieces and construct a simulation of how it was.
However, these simulations are imperfect because they take place in our current context. We can imagine how it would feel to win the lottery, but not what we would be like as people a year later. We can remember last Christmas, but our memory is colored and modified by what has happened since, by the current context in which we recreate it. This is a limitation: we can simulate events, but not context.
In software we have more power. This is the magic of the VM image: a complete context, so that when we start our program a second time, it will behave the same way as the first. Reproducibility. We can reason and make predictions about software in a way we never can about people, because the context of software is knowable and reproducible.