02 May 2020 •
Melvin Conway in his 1968 paper How Do Committees Invent? postulated the now-famous Conway’s Law.
Organisations which design systems are constrained to produce designs which are copies of the communication structures of these organisations.
This homomorphism between organisational communication structures and systems designed by them, has become an adage in software management. It implies a one-way effect, though. But, does it work in the other direction?
Given a mature (say, software) system, can we infer organisational communication structures? Particularly, informal communication structures? Do informal communication structures affect system design in the first place?
01 May 2020 •
Providing Streaming Joins as a Service at Facebook.
Jacques-Silva, G., Lei, R., Cheng, L., et al. (2018). Proceedings of the VLDB Endowment, 11(12), 1809-1821.
Stream-stream joins are a hard problem to solve at scale. “Providing Streaming Joins as a Service at Facebook” provides us the overview of systems within Facebook to support stream-stream joins.
The key contributions of the paper are:
a stream synchronization scheme based on event-time to pace the parsing of new data and reduce memory consumption,
a query planner which produces streaming join plans that support application updates, and
a stream time estimation scheme that handles the variations on the distribution of event-times observed in real-world streams and achieves high join accuracy.
Trade-offs in Stream-Stream Joins
Stream-stream joins have a 3-way trade-off of output latency, join accuracy, and memory footprint. One extreme of this trade-off is to provide best-effort (in terms of join accuracy) processing-time joins. Another extreme is to persist metadata associated with every joinable event on a replicated distributed store to ensure that all joinable events get matched. This approach provides excellent guarantees on output latency & join accuracy, with memory footprint sky-rocketing for large, time-skewed streams.
The approach of the paper is in the middle: it is best-effort with a facility to improve join accuracy by pacing the consumption of the input streams based on dynamically estimated watermarks on event-time.
.. Read More
20 Apr 2020 •
In The Design of Everyday Things, Donald Norman talks about the temperature knobs on his refrigerator:
I used to own an ordinary, two-compartment refrigerator - nothing very fancy about it. The problem was that I couldn’t set the temperature properly. There were only two things to do: adjust the temperature of the freezer compartment and adjust the temperature of the fresh food compartment. And there were two controls, one labeled “freezer”, the other “refrigerator”. What’s the problem?
Oh, perhaps I’d better warn you. The two controls are not independent. The freezer control also affects the fresh food temperature, and the fresh food control also affects the freezer.
In fact, there is only one thermostat and only one cooling mechanism. One control adjusts the thermostat setting, the other the relative proportion of cold air sent to each of the two compartments of the refrigerator.
It’s not hard to imagine why this would be a good design for a cheap fridge: it requires only one cooling mechanism and only one thermostat. Resources are saved by not duplicating components - at the cost of confused customers.
Norman is talking about the lack of a (good) interface here: a layer to translate (and hide) the structure of the underlying mechanism to the users of the mechanism. The need to translate to the user arises in two scenario:
- There is a divide between the want of the user, and the how the mechanism is structured. I like to call it the what-how divide.
- Although the mechanism & the user’s want are aligned, the mechanism is too convoluted for the user to use in a direct way. A facilitator is needed.
In both cases, a translation is needed, and the translator is termed an interface.
Languages are Interfaceless
(Inter)Faceless a.k.a No-Face
(Natural) Languages are the quintessential human way of communication. Our advanced languages are arguably the lone differentiators of our species from our cousins in the primate family, and the larger animal kingdom.
We have been inventing, honing, assimilating, and discarding languages since the start of our existence as a species. But we do not develop languages with an intent for it to be translated. Languages are not meant by its inventors to be translated. Every language is developed as if it is the only language in existence, and everyone else understands it.
.. Read More
18 Apr 2020 •
The purpose of an engineering organization (at the risk of sounding frivolously reductionist) is to build business value. You can grow an organization’s delivered business value over time by:
- training members: investing in people,
- improving process: investing in shaping behaviour and communication,
- staking technical leverage: investing in technology.
A cumulative side-effect of these approaches is to strengthen innovation loops.
Innovation loops are informal, intrapreneurial feedback loops in engineering teams which builds products & features to address user demand & pain. It is innovation which circumvents the software development cycle involving product & market research teams. In mature teams, innovation loops complement & reinforce the existing, evolutionary product development feedback cycle. I call product development evolutionary, in contrast to the more revolutionary (or reactive) trait of innovation loops.
Regular product development as green arrows; Innovation loops as red squiggles.
Innovation loops are more prevalent in infrastructure teams than in product-focused teams. This could be partly explained by the availability of direct communication channels to users which infrastructure teams possess, and product-focused teams do not.
.. Read More
25 Mar 2020 •
A recent exercise I undertook of upgrading Apache Spark for some workloads from v2.4.3 to v2.4.5 surfaced a number of run-time errors of the form:
org.apache.spark.sql.AnalysisException: Cannot resolve column name "name" among (id, place);
A little poking-around showed this error occurred for transformations with a similar general shape. The following is a minimal example to recreate it:
val df = Seq(
df.na.fill("empty",Seq("id", "place", "name"))
This looks wrong, but apparently works fine in v2.4.3 😲. A transformation which attempts to fill in a missing value for a column which does not exist should raise an error: v2.4.5 does that.
.. Read More