Posts

Showing posts with the label AFM

DSL as domain reference truth

 I have always been a very visual thinker, I tend to think in images and feelings, and less in words. Therefore having just reread my  Why Use Arrows and PowerPoint to Express Software Designs?  blog, which is all about using visual props to help you design systems, and remembering how ineffective these visual representations were in helping me share my ideas, I find myself needing to accept that my carefully crafted "invariants as drawings" approach have really mostly only helped me, and definitely were unable to express enough to others to drive a software development! The bright side of this story, is as much as at Actant, as later a Elevence, we had the insights to center development around a DSL. The team then having the freedom to express the needed properties in the DSL, I having the freedom to ask for something non-obvious by describing the expected behaviors, not the invariants, and especially not as visual projections that lack reading instructions. The bigger s...

The Engine of Approximated Certainty: An Introduction to High-Performance Software Engineering

Building advanced software systems by trial and error is a fast track to bankruptcy. Today, the cost of generating code has plummeted, but the cost of ensuring trust and verifiability has skyrocketed. To survive this economic squeeze, we can no longer rely on outdated, informal development cycles. Over the last few weeks, I have documented the mechanics, psychology, and processes behind a "zero-fail" methodology I have used for over fifteen years. I call it High-Performance Software Engineering . This post serves as your router and introduction to the series. It breaks down the six foundational pillars of this methodology and points you to the deep-dive articles that explain how to make it a reality. The Engine: A Product and DSL-Driven Process High-Performance Software Engineering completely bypasses the traditional Agile cycle of translating features into stories and stories into code. Because human translation always results in lost intent and bloated latency, we change th...

Symmetrical, Multi-Pass, Trace-Driven Compiler Pipeline in Python in two days

In 2011, after fourteen years developing derivative market making software, I took some time for myself, and wrote a prototypal compiler for a reactive trading language. I bring this up because a reactive trading language is a DSL, and I wanted to write something about DSLs in the context of my recent writing about  High-Performance Software Engineering . Therefore I asked Gemini to rebuild something similar as my 2011 effort, with four new twists:  the introduction of profunctors (I had only monads and comonads back then),  the use of combinators to not only to parse the DSL's syntax, but also it's traced execution semantics,  a JIT approach based on working with traces, This is Python. It was F# in 2011. What you have below is Gemini's description of two days of work. And to highlight the still very toy language stage of this language. The goal is show how advanced computer science can be done very quickly in 2026. From a language design perspective, what we h...

High-Performance Software Engineering Explained

Is failure ok? I was recently asked: " What companies have a culture where failing is ok "? I cheekily answered " None "!  The art of high-performance engineering This is the recipe I have been applying for the last fifteen years: Never f ail to achieve previously promised goals Never f ail deadline because of waiting on others Never f ail to use all your team members Never f ail to stay productive because of exhaustion Never f ail because of misalignments Never f ail because of past work no longer relevant, or past work never finished. To which mostly people say: are you kidding? There is a twist! These are extreme technology projects. Agile deconstructed An agile process typically maintains delivery drive and failure avoidance with process cycling over: Features (what we discuss with our stakeholders) Stories (what we work with) Code (what we deliver) These are primary driving efforts.  I skip the 10+ supporting efforts, such as validating, analyzing, etc. I skip ...

Meeting the many different communication structures in and across organizations

Sixt y years ago, Melvin E. Conway wrote: Organizations which design systems (in the broad sense used here) are constrained to produce designs which are copies of the communication structures of these organizations. — Melvin E. Conway, How Do Committees Invent? (quoting from Wikipedia) How does Conway's finding affect software language design? How does one specify a language that meets the many different communication structures of one or more organizations and yet still preserve formal properties such as security and provability? How do I make a language that does not end up communicated as list of hundreds of recommended design patterns? How does one zigzag between OO, FP, and other programming approaches? How is this related to machine learning and LLMs? Recently I was asked to help on a project. My immediate response was: Tricky, knowing what I know now, I would write a DSL to produce the necessary content for the project, but we cannot afford that for this project, so no DSL. ...

Software designs that grow: ten years of success

Having led the development of a large-scale, mission-critical system in a complex domain. I can't talk about the specifics of that system, but I can direct you to this architectural philosophy and design pattern blog I wrote many months before I started Elevence , and even before I wrote my playbook. It has become one of my most liked blogs:  Software designs that grow with monads, comonads, and type compatibility  . And one that has given me some fame. The story was that after having built software for many years, I started to notice that certain designs inevitably collapsed under their own weight, while others seemed to grow stronger with change. It felt less like a matter of style and more like a matter of mathematics. There are underlying laws about how information and change behave in a system. I wrote up the thoughts in that blog. Purposely cryptic back then, as it makes no sense to give away what is still new. Breaking down some core concepts of the blog: Type...

Math over types

Image
I use to be a believer in type centric software designs. Heck, I hired and led a team of Haskell developers to implement a smart contract language ! However, just like software languages get reduced to numbers when they run on computers, types can also be reduced to numbers. Yet that is not enough as types capture invariants, therefor more than numbers are needed to implement types. We need numbers and math! The figure below is a seven dimensional binary space partitioning of one hundred random segments. It is a modern re-visit of code I wrote in summer 1984 for an integrated circuit CAD tool. Here I want lots of dimensions, because types as numbers need them. Focusing on our topic:  Shallow designs are better. Yet shallow implies wide. And wide is hard.  Monolithic design are by nature local. Adjunctive, multi-dimensional designs, can be distributed. Yet they are also hard.  Math is good at dealing with hard.  To wrap this up:  In 2024 and onwards we do no...

Software with formal properties at a low cost through monads and co-execution

Image
Not so long ago a good friend of mine mentioned his need for 'formal properties' in his code.   I asked him if he had already made the code purely monadic. He had. "Good", I said, "because the 'cheap route to formal properties' is first to ‘monadize' your code, and then 'reverse it' by bringing in 'co-execution'".  That last part needs some development.  Formal properties are not only about execution, actually mostly not about execution, but more about formal properties of state. So while you do need to show execution invariants, you often much more need to show that the ‘results’ happen within stateful invariants (e.g. of the ledger). The easiest way to do this is to  deconstruct / co-execute the state model backwards. This 'backwards' logic must 'match-up' with the 'forward execution' (monadic) model. My view has always been comonads are easiest for this, especially if your stateful model (e.g. a blockcha...

Why Use Arrows and PowerPoint to Express Software Designs?

Image
Dog Drives Car  Silly me shared the following picture with my sister-in-law yesterday, as she was stuck in a massive traffic jam with her dog (in photo) in her mini.  "How did you make the picture" was the question. "PowerPoint" was my reply. I used PowerPoint for that photo because it took me three minutes, and I had a slide deck open. Stop, you might say, this has nothing to do with programming, or software designs! It does if we want to design a software where a dog drives. Yet how would we approach that? Here I tell you how I have designed and communicated designs of 'big and bold' softwares quickly and with good properties. And will also sketch out how to approach exotic requirements, such as dogs driving cars. Arrows Away! Let me show you how I work. This how I use to draw design 28 years ago (from my PhD thesis): This is how I would have drawn that design 18 years ago (~2004): Here I use arrows to represent flows. Arrows are used to capture the Lagran...

From pure effects and observations to social theories

Philosophy in computer science Sometimes philosophers ask questions that seem of little use, for example: "If a tree falls in a forest and no one is around to hear it, does it make a sound?"  While pointless to most of us, this is in fact a central question for computer scientists. Let's rephrase for them: "If effects happen, and are never observed, did these effects really happen?" The simple answer from a computer science perspective is "No, effects only exist when observed". The more complex answer would have us questioning things. We might ask: What does it mean to "observe"?  What is an effect?  Can an effect sometimes be real, and sometimes not? Is a virtual effect still an effect? Is a virtual observation still an observation? What about the heat generated by effects that are not observed? What has this to do with security vulnerabilities like Spectre and Meltdown? ... I will not ask these questions, or at leas...

Software designs that grow with monads, comonads, and type compatibility

This post is about designing software with internal and external APIs that are robust to future changes. It is therefore about API compatibility, but more importantly it is about the compatibility of a full software design to changes. Not surprisingly, monads and comonads are part of the presented solution, as is a careful approach to use of types. I had a "aha" moment last year when I watched a video (by Timothy Baldridge) that showed how an AST for a Clojure compiler was fully based on key-value pairs (nested hash maps), therefore without typed structures nor classes, and was doing very well. The thing is, I have suffered enough times to get the types of the different phases of a compiler to fit together. Therefor the idea of giving up on types and just using key-value pairs, that can easily be added for each compiler phase, seemed really to be an attractive way to write a compiler. Key-value pairs, duck typing, and many "traditional" functional patterns (...

Eventual consistency: dual software models, software with gaps

Maintaining consistency within parallel, distributed, high-performance, and stream oriented programming is all about accepting one thing: when it comes to consistency, that is dealing with failures, don't promise too much too soon! And that is the whole notion of eventual consistency: you sometimes need to wait for things to get resolved. "How much you wait" depends typically on how much money you have invested in infrastructure, yet it also depends on how fine your system captures failures. Let's start with the eventual consistency.  In traditional transactional models, consistency, the knowledge that your system is "doing well", is assured by approaching each operation "transactionaly": if an operation fails, make sure nothing is messed up and that consistency is maintained.  Yet in the "new" world of massive parallelism, need for high-performance, and the like of streaming models, it is often simply impossible to maintain consistency...

The beauty and the beast: explicit and implicit types

Working with Haskell has brought on an unexpected question: Is it more productive to work with implicit types or explicit types? By this I mean: “Should major types be mostly inferred or mostly specified with type signature”? The bizarre fact is that you may well use two different parts of your brain to work with inferred types or with “defined” types!   Here is the reasoning: Programming is an incremental process: changes are made locally and then overall consistency is assured. When programming around types, changes are made to type specifications or to expressions which affect type. The two kinds of changes lead to two styles of programming: the first is to start by changing a type definition or type annotation, the second is to start by changing an expression, which results in a change to the inferred types, and possibly needing a fix to a type signature. The bare minimum of type annotation is not even to provide argument to “top level” functions, such as ...

Personalize your containers (in F#)

Many years ago I implemented an augmented tree structure in C++. Inspired, I decided to implement the structure in F#. FYI by augmentation I mean storing extra data at each node of the tree that will accelerate the later operations on the tree. In DB terms, augmentations are typically used include secondary key conditions in your tree query. This is what I did: Take a balanced tree implementation off the web. I was thinking finger tree but then though better to start with a simpler implementation. I chose an AVL tree. (I may revert the code to a red and black tree to simplify). Add a node field to store the augmentation (a generic type) Refactor the code by replacing the call of the node constructor by a call to a function that will first build an augmentation and then call the node constructor Pass the generic augmentation "maker" function as argument to the function calls On this last point I wasted much time. Initialy, I tried to encapsulate the "augmentation...

Types versus architecture

There are three invariants in a software system: Code Types The architecture One old rule is: more types, less code. This is because the less "sophisticated" type provides less "support" to the code, and therefore you need more code to keep things in control. Another way to put this is that the invariants provided by the use of more types allows the code to be more simple. For example, long ago I experienced this type of code simplification with the use of the record/struct moving to C and Pascal from early basic and FORTRAN (and assembly). And again with classes and objects moving to C++. And again with variant types and later monads with functional programming. Another rule is: better architecture, less code. I have repeated this mantra for many years and yet thinking about it, I am not even very sure I have a good definition for architecture. I use to say: 'architecture is what does not change in the system'. Now maybe I should say: 'it's what d...