Let’s start with a tangentially related preamble. I answered this Quora question in September 2012 with:
Optimize: automate. Replace (part of) yourself with very small shell scripts.
Scale vertically: become more efficient. Experience and training can help you perform the same tasks faster.
Scale horizontally for writes: shard. You can either delegate subtasks to others (divide and conquer / master-worker style) or just split your work between people at the same hierarchical level (my favorite solution, but remember collaboration is hard - graphs are inherently more complex than trees).
Scale horizontally for reads: add redundancy. Teach others how to do your job. This also increases availability and tolerance to failure by augmenting your bus factor.
Some of those things may or may not change with AI, but the point is: as long as I have been working, my worry has not been having nothing to do; it has always been dealing with too much.
Several people have asked me that question recently. Some are programmers, some are not.
They ask this because they do not understand my job. At its core, it is about reducing workloads through optimization and automation.
Sure, I work on AI now, but that is not the important part. In that respect, AI is just the updated version of a very small shell script.
So let me answer that question. Yes, maybe someday robots will take my job. But if that happens, it means they will have taken yours too, because otherwise my job will be to make it happen.
Not everybody agrees, but personally, I am enthusiastic about the end state where nobody has to work. Sure, this is a turning point in the history of mankind and we will have to adapt to the state of abundance, but we will still be much better off than we are now.
So, do I worry about my job? Absolutely not. What worries me is the transition period when I have a job and you don’t.
P.S. — I am being provocative on purpose. There are things I hope we never automate. But ask yourself: if money was not a concern for you or anyone else, what would you do? This is what I want for everyone in the end. (Seriously, go read Manna now if you haven’t yet.)
I believe that most homes in the developed world will have a domestic multipurpose — probably humanoid — robot in 10 years. This is not the consensus in the field; I often meet people who hold the opposite position, but debating that is not the point of this post. Instead, let us assume it will happen and consider what it will change.
The first thing a new versatile tech product does is almost completely replace more specialized products. Before smartphones, we had portable GPS devices for cars, MP3 players, PDAs, compact digital cameras, and of course non-smart phones. Some things are only partially replaced, for instance credit cards, loyalty cards, calculators, agendas, portable gaming consoles, and alarm clocks.
So what would go away if we had general-purpose robots? The first category I can think of is specialized robots such as cleaning robots, kitchen robots, and lawn mowers. But it would probably also directly replace less obvious things such as home alarms, and possibly partially pre-cooked food — see later regarding that.
Beyond products, new technology often replaces or diminishes the demand for certain jobs. AI is already doing that with many white-collar jobs, but giving it a bodily extension will go further.
It may not be the case for the first models, but I suspect after a while, a robot will be able to fix a simple leak, paint a wall or do simple electrical wiring jobs given the right tools. It should also be able to perform the yearly inspection of your boiler and pipes — although regulatory changes will probably take longer. Maybe eventually some simple medical acts too (intramuscular injections, changing a bandage…).
But the most impactful part will probably not be the jobs that will be replaced; it will be the things most people did not do. To find those out, just look for what people wealthy enough to pay for full-time help get that the rest of us don’t. Bringing what used to be a luxury to everyone through automation is textbook disruption.
Why would I buy pre-cooked food if I have a robot that can make me home-cooked meals? It could go to the market to get fresh products daily, or have them delivered. It could clean the kitchenware and take out the trash.
Also, we all have those small imperfections in our homes that we don’t fix because they are not worth the time. That small defect that should be painted over, that slightly moldy sealant that should be replaced. A robot could vacuum and change your sheets daily if you want it to.
Oh, and of course I think robots could save lives too. One obvious way being calling 911 and performing resuscitation if you have a cardiac arrest. I also think having robots at home can help the elderly stay out of nursing care longer if they want to.
In general, I am extremely enthusiastic about home robotics. I want to “live in Star Wars” — well, the peaceful part of it. I know it is likely to make me underestimate the issues it could cause and the difficulty in making it happen.
Like AI, this is a field where predicting anything beyond a few years is very hard. There are many unsolved problems to achieve all this, and the order in which they will be solved is not obvious. However, I think it is worth trying to anticipate these changes.
“Thoughts & Links” are posts mixing topics on my mind and interesting things I have read recently. Less thought out than regular posts.
Agent-first documentation, with humans as an afterthought. But like the mobile-first web, it may actually be an improvement for humans as well in the end.
Towelie is a code review tool for code written by agents. I have been using it for large changesets. It gives you a code review UI and spits out a prompt to paste into the agent CLI. A simple tool that does one thing well.
Renan is the CEO of OSS Ventures, a startup studio for industrial B2B software.
He posits that what hinders reshoring is labor cost, but not that of blue-collar workers. Today the white-to-blue-collar ratio excluding R&D is above 1, and white-collar labor is more expensive. The way to make factories competitive is to lower that ratio significantly thanks to modern tools.
Nobody knows. If you think you do, you are delusional.
Personally I bet that I will still have a job, but it will be very different from the one I have now.
A tweet by Z.ai, which is one of my favorite labs currently.
I think they are right, as recently demonstrated by Anthropic tightening its limits. Infrastructure is becoming crucial for resilience and cost-efficiency.
Lou of Z.ai has also posted her personal view that OpenAI is investing in infra whereas Anthropic outsources. I do not completely agree, Anthropic did order a million TPUs from Qualcomm, after all.
Several people have posted similar things on that topic, including Stanislas Polu:
They’ve deprived us from the state of “flow” that many of us were thriving for. […] Coding with agents is an interruption management game. […] The excitement remains “building” shit, and more of it.
… and Julien Danjou:
I chose throughput over depth, and I keep choosing it every morning.
A generation of developers is about to start their careers with AI from day one. […] If you’ve never held the full system in your head, you don’t know which questions to ask, and you won’t catch the bugs that live in the gaps between modules. […] I chose the trade anyway, because the leverage is real, even if the loss is too.
The CEO of Antithesis with one of the best posts about company culture, how all organizations lose what makes them different as they grow, and how to slow it down.
(No, despite the URL, this is not about Rust.)
I know this all too well, I have known it even before I started working because it happened to communities I was a member of as well. This is part of why I have only worked at small companies.
In any case if you have to grow, read that post, it is full of insight. But maybe you don’t have to? See next link.
AI solves communication in large organizations… by removing large organizations.
This is one reason AI-native startups are pulling ahead, and why building AI companies feels fun. The advantage comes from organizational structure. Fewer humans, fewer channels, faster iteration, compounding speed.
Sometimes a product is funded just well enough to exist, but not well enough to be loved (like many enterprise-grade box-ticking features) and there’s nothing the engineers involved can do about it.
When a technology finally clicks, the changes spread faster than anyone expects and have far more serious implications for society. I want to lay out a case here for robotics finally “clicking” within the next few years, and what that could look like for everyone. […] The level of investment and growth in robotics and AI is reaching a fever pitch, well beyond what I expected 1-2 years ago. And, perhaps more importantly, most of what I have believed are substantial blockers to robotics deployments now seem solvable on a technical level.
Every layer of review [or process approval] makes you 10x slower. […] AI can’t fix this. […] The only way to sustainably go faster is fewer reviews. […] But you can’t just not review things!
Review pipelines — layers of QA — don’t work. Instead, they make you slower while hiding root causes. […] But, the call of AI coding is strong. That first, fast step in the pipeline is so fast! It really does feel like having super powers. I want more super powers. What are we going to do about it? […] I think the optimists have half of the right idea. Reducing review stages, even to an uncomfortable degree.
Imagine an old-school U.S. auto manufacturer buying parts from Japanese suppliers; wow, these parts are so well made! Now I can start removing QA steps elsewhere because I can just assume the parts are going to work, and my job of “assemble a bigger widget from the parts” has a ton of its complexity removed. I like this view. I’ve always liked small beautiful things, that’s my own bias. But, you can assemble big beautiful things from small beautiful things. It’s a lot easier to build those individual beautiful things in small teams that trust each other, that know what quality looks like to them.
This is why good teams right now are a few real seniors augmented with AI. Because a defining trait of seniority is knowing exactly where and when you can cut corners and when you need extra confidence. And if the whole team is senior then you can trust your colleagues with that. Of course this is not very sustainable…
Maybe tooling, including software verification, can help with this too (see next link).
The problem is not that everything is broken. It is that AI is changing the scale and speed of software production faster than our ability to verify it. What works at human pace may not survive AI pace.
The productivity gap is widening: teams with the best tools are pulling further ahead while others stagnate. Verification will be a decisive advantage.
Mistral also shipped a coding agent designed for Lean.
Many of the heuristics that we’ve developed over our careers as software engineers are no longer correct. […] What it means for a system to be maintainable. How much it costs to write code versus integrate libraries versus take service dependencies. What it means for an API to be well designed, or ergonomic, or usable. What it means to understand code. Where service boundaries should be. Where security and data integrity should be enforced. What’s easy. What’s hard.
Your taste, your high standards, your understanding of your business and customers and the deep technical trade-offs in your area are more valuable than ever before. This is like that fantasy that people have of going back to middle school knowing all the things they know now. You’re ahead of the pack in many ways. But you also need to really deeply question the things you know, and the things you assume. Before you share one of your rules of thumb, you need to deeply examine whether it’s still right.
Over the next couple of years, the most valuable people to have on a software team are going to be experienced folks who’re actively working to keep their heuristics fresh. Who can combine curiosity with experience. Among the least valuable people to have on a software team are experienced folks who aren’t willing to change their thinking.
The resistance to AI-assisted software development among experienced software engineers isn’t random or capricious–it follows the pattern Thomas Kuhn identified in scientific revolutions sixty years ago.
The paradigm is shifting. The evidence is in. The question is not whether the shift will happen, but whether the people who built remarkable things within the old paradigm can find their place in the new one. Kuhn would say probably not. Planck would say wait for the funerals. But the compressed timeline of this particular revolution, the example of the likes of Armin Ronacher, means there’s still a choice available.
Almost 20 years ago, when I was still an engineering student, a retired network engineer from the ITU told me:
An engineer does what a technician can do, but 30% more efficiently and for 50% of the cost.
I always liked that definition of engineering, but only today did I figure out — thanks to Marc Brooker — its likely origin. It is a quote from Arthur Wellington:
It would be well if engineering were less generally thought of, and even defined, as the art of constructing. In a certain important sense it is rather the art of not constructing: or, to define it rudely, but not inaptly, it is the art of doing well with one dollar, which any bungler can do with two after a fashion.
“Thoughts & Links” are posts mixing topics on my mind and interesting things I have read recently. Less thought out than regular posts.
This changes so fast I think I’ll include an update in most Thoughts posts. The previous update is here.
At work, I use Claude Code exclusively. My editor is now almost read-only, I even do most of my single-line changes through the agent so it has them in its context. I mostly use Opus 4.6 but sometimes reduce effort to improve latency.
My current “free and personal” setup is GLM 5 in pi through OpenCode Zen. It is slower but mostly as good as Claude. I have tried the other two free models available (Kimi K2.5 and MiniMax M2.5), and I much prefer GLM 5. I still use Amp Free sometimes but the credits don’t last long.
EDIT (2026-02-26) — GLM-5 is no longer free on OpenCode Zen (they introduced an OpenCode Go plan for $10 / month). It is still free on Modal until the end of April. To use it with Pi, go here to obtain a token and put this in
~/.pi/agent/models.json:
{
"providers": {
"modal": {
"baseUrl": "https://api.us-west-2.modal.direct/v1",
"api": "openai-completions",
"apiKey": "modalresearch_YourToken123",
"authHeader": true,
"models": [{ "id": "zai-org/GLM-5-FP8" }]
}
}
}
If AI absorbs all the shallow work, the only things left that genuinely require a human are the core parts that demand genuine creativity, judgment, taste, and the type of thinking that can’t be prompted away. […] That kind of deep creative work is best done away from the glowing rectangle.
The problem is that I no longer get bored. That is bad. I need to get bored so I can start to imagine, daydream, think, self-reflect, plan, or even get mentally prepared for things (like the Stoics talked about). I badly need that empty space back.
AI does actually make you 10x more productive, once you learn how. […] But who actually gets to keep that value?
It would seem that we are addicted to a new drug, and we don’t understand all of its effects yet. But one of them is massive fatigue, every day. […] We’re all setting unrealistic standards for everyone else. […] That’s a race that ends, in my opinion, with everyone collapsing in exhaustion without actually winning the race.
Steve’s argument is that if AI makes you 10x more productive and you don’t change your salary or working hours your employer captures 100% of that value, whereas if you work 10x less you capture 100% of the value.
It’s not even remotely sustainable for companies to capture 100% of the value from AI. And when employees capture 100% of the value, it will be temporary at best: that company gets beat by someone who’s got the dial turned higher. […] The right setting is in the middle somewhere. Companies will try to drag it higher. You need to fight to drag it lower. […] The new workday should be three to four hours. For everyone. It may involve 8 hours of hanging out with people. But not doing this crazy vampire thing the whole time. That will kill people.
This and watching the Claude Code team take feature requests on X and implement them the same day make me consider one way software development team balance should probably change in the short term for user-facing products: support should become much more skilled and important.
Years ago a French startup called Capitaine Train (since acquired by Trainline) was famous for having skilled product people work support and use the feedback to improve the product. I have long admired this and pushed for similar approaches (e.g. having developers participate in support rotation to fill their pipelines).
But now if we can eliminate the developers bottleneck for simple bug fixes, this changes the game. We are close to a point where fixing a bug reported by a customer and confirmed could be a simple button click in Intercom. That would be huge for complex products such as Inch.
In the section above I described a short-term solution to improve products that are complex because of the 80/20 myth. But on a slightly longer timescale I think we can just fix that problem entirely, at least on the frontend. I wrote a LinkedIn comment in French about this.
The complexity comes from customers using a different subset of the product. But if the cost of creating an interface becomes close to zero, customers could just create their own. SaaS products would then just become APIs that customers could query and integrate exactly how they want using agents.
I know it works because I do this for myself. I have a vibecoded personal Web application that is a kind of dashboard where I integrate features from several SaaS as I need them. Note that when I say vibecoded I still read the code to some degree, this is not accessible to non-developers yet. But to a small company with a single developer, I think it is.
I’m not worried about AI taking over. I’m obsessed with it. […] The fundamentals I learned the hard way? They translate perfectly to telling Claude or ChatGPT exactly what I need. The key is knowing what to ask for. Twenty years of programming principles don’t disappear, they become the foundation for working with AI effectively.
Brex realized that speed and execution shouldn’t come at the cost of increasing headcount.
Honestly, that has long been true and I have defended that point of view my whole career. My about page has been saying this for ages:
I believe small teams can achieve a lot. Do not ask me to manage the 10x growth of your engineering team, ask me to find ways to prevent that from happening in the first place.
If AI helps companies realize this more easily, all the better.
Joy is a valid reason to choose a language, even with less AI support. It’s just not my focus right now. My joy resides in solving problems, and I want tools that maximize my leverage. For that, conventional wins.
Agreed on all counts, except that I think agents are actually reasonably good at using exotic software. For instance, the personal web app I mentioned earlier is written in Teal using mote. In my experience the worst is stacks that are somewhat popular but with unstable APIs (e.g. Zig).
In any case, a lot of people are left wondering if code was just a tool or something more, and about the role of fun (David in French, Ori Bernstein, David Cells…).
Personally, I already touched it here: it is both. I have always needed both a goal and some enjoyment. I don’t think the agentic switch will necessarily change that.
Many times, you try to make a project take off that by definition nobody asked for. You are convinced that if you do it people will go “Oh great! I can do something awesome with this!”, but nobody will ask for it until it exists. And the problem is at a large company you’ll be asked “can you show me figures that prove it is what we should do?” and you go “Well, no. I have to do it first.” And that’s scary for large organizations because it’s a jump into the unknown, […] even at a small scale.
Typical. When it can be tested in a short timeframe the solution is “ask forgiveness not permission”. As soon as you mention it you get into arguments that will take you more time than actually prototyping.
For things that take more time it is not always possible, and that’s when you need to play politics to get buy-in. But when you can, (vibe)code and ask later. Remember Claude Code was a side project.
In some sense, building software that can do financial, political and legal analysis is the latest weapon in the arsenal of the computer people. Many despair about what AI might do to software developers: I recommend looking at it in the context of the hacker culture war.
A beautiful post about the true value of college. Few students appreciate it in the moment. I think I did, but not as much as I do now. The author has founded two startups in the meantime, it clearly gave him the necessary hindsight.
I often wonder how we can realistically emulate parts of this later in life. Recurse Center is a step in that direction.
Extract translated from French (original title: De l’artisan au prolétaire).
Note that Karl writes extremely well and I am not fluent enough to translate how it feels. If you can read him in the original French, do it.
Mastering tools is not merely a level of attainment. It is also a trajectory, especially in an accelerated world.
We do not turn into wage-workers when we delegate our expertise to the machine. It happens when we give up (or are forced to) our desire for expertise. When we give up on discovering and mastering new tools while enslaving ourselves to a political program [including capitalism]. I will not name the specific tool, it is a distraction. It prevents us from thinking globally about what is happening under these circumstances.
Yesterday, Anthropic posted this and IBM stock instantly lost 10%.
I have no doubt that agentic programming will be a game-changer to escape legacy systems.
What the Anthropic post describes is the way professionals already deal with legacy systems:
This is called the Golden Master pattern.
The challenge, however, will be to give human stakeholders high-level indicators and tools to understand the progress and ensure everything is going well. And then a way to identify and fix the defects that will slip through the net and end up in production.
I have no doubt that AI, used by capable people, can help with all this. And this is not only about COBOL; I have recently met the founder of a (still stealth) startup tackling a similar problem with a different stack. The total market for legacy software migration is unfathomable.
Maybe Anthropic is still undervalued after all!
Note: Yes, I use em-dashes when I write, and no, this is not AI. As always I only use “AI” for spellchecking.
I have been at Finegrain for three years so it is time for the next article in this series where I share a bit about what I’m doing at work.
I am going to skip things we have done in the early days that have become irrelevant and focus on what we are doing now.
So first, we make image editing models. We train them, we evaluate them, and we ship them in a format customers can integrate. We have always focused on keeping the models small and efficient, initially so we could have a fast and cheap API, and now so they can run entirely on edge devices such as mobile phones.
We sometimes build end-user tools to demonstrate those models too. Among those you may have seen in our API era, there was a Web-based image editor, ComfyUI nodes, a chatbot for image editing, and an ad generation assistant. All those are gone, but we still have a few public Hugging Face Spaces. Now our flagship product is the mobile SDK, we have a free mobile application to demonstrate it.
Now, let’s get a bit more into what I am working on exactly.
The first important thing I have built is our server-side inference stack. What does this include?
A model is a bunch of layers, which basically means matrices of numbers corresponding to operations (multiplications, convolutions, etc) organized into a graph with non-linearities sprinkled in-between. The model takes tensors as input and returns tensors as output.
When you want to do something with a model, you need some preprocessing to feed it data and postprocessing to take it out. In the case of text, that often means tokenizing. In the case of latent image models that means encoding and decoding — which by the way also involve models.
You also need a lot of things to optimize the model’s operation. That includes the mode of execution (compilation, quantization, numerical precision, etc). Crucially, in the case of models that you call several times in a loop such as diffusion / flow matching models, this also includes solvers (samplers / schedulers), which can be the most complex and math-heavy part of an inference pipeline.
In our inference stack, which is called PHX (a reference to the Phénix airplane), all of this is packaged in a processor (which some call a pipeline). Processors can be used locally or deployed to Modal — which we use e.g. for model evaluation purposes — but in production they are deployed on GPU servers and exposed to callers using NVIDIA’s inference server Triton.
PHX is not exposed to customers or applications directly. We provide a Web API that is easier to call, asynchronous (because although our inference is fast it is still slower than a typical Web request), and deals with authentication and payment.
In addition to this API, we have a Web backend for customers, a bit of admin and monitoring… All of this lives in a modular monolith called Disciple, a reference to a character of the comic strip Léonard whose most famous line is “I serve science, and that is my joy.”
The stack is nothing too fancy; I picked technologies I had previous experience with and knew to be reliable. We use Quart with Blueprints and PostgreSQL. For asynchrony we use Nchan for SSE and Beanstalk for background jobs. For efficient image processing we rely on VIPS.
For some time now, the main focus of Finegrain is running all our models on mobile devices directly. This means we had to port our inference code to run on iOS (for now) using Apple’s Neural Engine (ANE).
This involves, among other things, compiling and quantizing the model using Apple’s Core ML Tools. This is not a straightforward step; most models cannot be compiled out of the box and require architectural changes first. As for quantization, obtaining high average quantization ratios while retaining precision is a craft that would deserve its own blog post.
Once you have that model, you need the equivalent of PHX’s processors, but for the mobile device. I use PHX as the reference codebase, but porting to Swift on iOS is not as straightforward as one may think. On mobile anything can quickly become a bottleneck, and if you are not careful you end up spending more time doing pre- and post-processing than running the model. We use Apple’s Accelerate framework extensively to speed up those parts.
All of this is advanced but straightforward engineering, but if you want to be good in this field you need to go beyond that and implement cutting-edge research or innovate.
A large part of it is in model training and the datasets we use, of course. I am hardly the person who trains the most models at the company — I do a little, mostly focusing on performance-related things. We all read papers in the domain and discuss approaches to try all the time though. Personally, I have been studying the things “around” the main model a lot, including solvers, NFE (Number of Function Evaluations) reduction techniques, and recently auto-encoders.
In addition to that, there are a few ideas I have had and implemented that make a difference. The most important one is the set of techniques we use to edit high-resolution images with a model backbone that works at much lower resolution. We call them “fixes” and let’s just say having a background in signal processing and classical computer vision as well as a good understanding of latent spaces helps a lot there. I have spent — and still spend — a lot of time on this, and it is a crucial part of what we do, so if you were to ask me what I am most proud of so far, that would be it.
Another notable thing I have implemented recently is the ability to swap the capabilities (skills) of the CoreML model. Core ML has Multifunction Models, but in theory they are built by compiling each function and merging them once on a Mac. With the tooling I have designed, we can create new functions for a given backbone in pure PyTorch, and add / remove / swap functions in the model almost instantly on any machine including an iPhone. This makes it possible, for instance, to update a single function in an application without re-downloading the whole model.
As always, I haven’t talked about everything I do. We are a small company and I am still a jack of all trades kind of guy, so I always write tooling and fix bugs here and there. That should give you a decent idea of what my day-to-day is about. As always, if something piqued your interest, feel free to get in touch.
It’s been that time of the year, again. No Denis and no Hisham this time, but I still got to spend time with other FOSDEM regulars Gawen, Benoît, and Thijs. I also met Corentin, Sylvain and Mehdi, and got introduced to Hugo.
Here is a list of talks I saw:
This year I didn’t write the list above by hand. I gave the URLs to an agent and the format I wanted and told it to make it. It’s a detail, but I do things like this many times a day now.
I also had agents work for me over the FOSDEM weekend. They ported a personal web application I have from a Lua Web server to another Lua Web server, from a CSS framework to another CSS framework, and added end-to-end tests with Playwright. Oh, and the application is in Teal so they wrote the definition files for mote too. All I had to do was describe the task and nudge the agents in the right direction in between talks. If I did not have those agents, I would just never have done all this.
At work, I’m back to training models this week, after a long time working solely on inference. Going from zero to GPUs going brr with nontrivial things like adversarial learning and brand new, (almost) working experiment tracking (because OpenAI is killing the old one) took me roughly a day.
And that was with the old agents. I can’t wait to see how well my new minions do tomorrow.
“Thoughts & Links” are posts mixing topics on my mind and interesting things I have read recently. Less thought out than regular posts.
This was true before, and this is even more true for the coding agents era.
I think agents are improving on that front, and Claude does it less than the others, but I still instruct them to fail early and loud and pay attention to this in reviews.
Some things in this post match my architecture principles.
To scale efficiently, you need cross-functional, independent teams with ownership. This is applied Gall’s Law.
Shorten feedback loops.
Instead of asking: “how long will this take?”, ask: “Can we make this smaller?”
For adults, AI is a tool. For Generation Alpha, it’s an extension of thinking. They’ve deleted “impossible” from their operating system and replaced it with “not yet.”
Mozilla is still doing some interesting things. I will follow this with attention, especially the WebNN part which is relevant to my work. I only work on native on-device AI for now, but we will probably tackle the browser as well at some point.
Found through Pierre’s 2025 recap. I don’t know how I did not follow his feed until now, I follow most current & former Ink & Switch people. Catching up with three articles.
Good designs expose systematic structure; they lean on their users’ ability to understand this structure and apply it to new situations. We were born for this. Bad designs paper over the structure with superficial labels that hide the underlying system, inhibiting their users’ ability to actually build a clear model in their heads.
This is a twist on that idea by Alan Kay that I have long supported. I like that way to present it. I think it plagues many modern products. AI could change this for better or worse; I hope for better but fear we’ll get worse…
As AI makes a lot of things easier, it’ll be interesting to ponder what kinds of new frictions we’ll want to intentionally add to our lives.
A thought experiment about the effects AI will have on society. I ponder this often these days, regarding robots as well. I like that Geoffrey does not frame that as “progress will have bad side effects so we should stop it at all costs”, but instead more as “progress will happen, it will be better in a lot of ways, but we should think about how to offset the parts that will be worse.”
My current goal with AI coding tools is to spend 100% of my time doing stuff that matters. […] When I sit down for a work session, I want to feel like a surgeon walking into a prepped operating room. Everything is ready for me to do what I’m good at.
A good metaphor for one approach to using coding agents efficiently, which is the one I am aiming for as well.
Yes, I have written about this recently, but it has changed so much that it warrants an update.
In the meantime we have tried Codex (5.1) for a few months at work. I did not like it, mostly because it is much slower than Claude. We finally went back to Claude Code and Opus 4.5 last month, and this is night and day. I am doing almost everything in Claude Code; there are days I don’t even write a single line in my text editor.
This configuration is basically as good as a mid-level developer, except it does what would take said developer hours in minutes. This means you can give it a task and immediately verify the outcome, then tell it what to improve without switching context and losing your mental model. Once you have figured out how to prompt it efficiently, it becomes a productivity monster. Its usefulness still depends on the task, but I have done things in half a day that I would have estimated to take at least a week without an agent.
For personal projects, I use Amp Free — which is excellent as well — but I still write most code myself due to the nature of the projects themselves.
I am very happy with Claude Code and do not intend to test other coding agents, except maybe Gemini if we get access to it on our plans. I am also curious how good GLM 4.7 in Claude Code would be if I needed a cheaper option. But for now, spending up to $200 / month for such a productivity boost is clearly a no-brainer. Finally, I will probably give beads a try one of those days, I can certainly see how it could help Claude with some tasks.
I live in France, I work on AI, and I am worried.
I believe that technical progress is inexorable in a globalized world. If a scientist makes a discovery and does not disclose it, another will make the same one within a few years. If a company forgoes a major innovation it will be disrupted by competitors that adopt it.
This makes research and innovation a game — in the game theory sense — in which not participating is not an option. You cannot stop the flow of progress, you can only decide to be an innovator, an early adopter or a laggard. You either lead or you endure.
Whether you like it or not the current AI wave will cause seismic changes. It is time to revisit 1970s science fiction because we are about to live in those worlds. And by their nature, some of these advances are likely to be winner-take-all.
Dropping out of the race for ethical or social reasons is short-sighted. It is very likely that whether the end result is good or not the first to reach these milestones will benefit immensely and strengthen their ability to influence the future. If you slow down, you are simply letting those who do not care about these issues in the first place win.
The French political leadership and that of Europe have a well-known track record of hindering innovation through regulation. We are the poster child for the precautionary principle. In addition to that, we currently face political unrest and our public opinion is easily swayed against anything new or successful.
The EU Commission has apparently noticed the problem. They are doing things, but it is too little too late. At the current pace, the goal should not be to let our industry do the things the US and China were doing five years ago.
I live in France, I work on AI, and I am worried. Maybe you should be too.
This post is inspired by a similar post by Sean Goedecke. I said I should do the same; here it is.
Note that the fact that this page exists does not mean I am looking for a job! Also note that all of this is true at some point in time and subject to change — I may update the post.
This means I want to work directly on the product my employer is selling or something that makes it possible. Support functions matter and I have huge respect for them but they are not for me.
My sweet spot is working on B2B products and interacting directly with customers.
Historically, my specialty is Distributed Systems, but I happened to work on AI (for Computer Vision) “before it was cool.” I left the field, re-joined it, and I intend to stay in it for now.
Note that this does not just mean using LLMs through APIs. It means training, operating and/or optimizing models. If reading and implementing research papers is not part of the job description, I am not interested.
I believe close-knit teams that meet regularly learn to work better together. I know some people believe this can be achieved fully remotely but I do not. However, I also believe working from home part of the time has benefits including cutting down on commute and better focus.
This is not a hard red line, I could go with full remote or full in-office under some conditions. My location is though: my life is in Paris, France and I am not considering relocating anywhere.
I do not want to work in finance-related fields, including banking and crypto. I would also like to avoid working for HR / recruiting companies.
Fields that specifically interest me include energy and physical automation / robotics. More generally, industrial fields involving non-standard modalities.
I am highly autonomous and self-directed. All my direct managers so far have been the company founders. I can deal with some politics, but not much.
In most cases I prefer being an IC with a Staff+ role. I am fine with having a few skilled IC reports; I do not manage managers.
I recognize the necessity of process, but I highly dislike its abuse.
I think small teams with real ownership work best.
I think people who care can — sometimes should — disagree and argue passionately, and still remain friends.
Since 2017 I have maintained a short list in a file called BELIEFS.md. It is somewhat similar to my Software Architecture Principles but for things not directly related to programming. I have decided to move it here.
Because opinions should be (somewhat) weakly held I have updated it from time to time, and I intend to do the same here.
Caring about one’s work over time is the most important thing to be good at your job.
Why did the robot do that? will be a very important question in the years to come.
Transformative innovations typically come from technologists. They are often introduced against the will of people in positions of power. Their authors initially do not understand the scale of what they are doing.
In B2B, knowing the product (its metrics, its users…) and the market has significantly more value than the product itself. (added in 2020)
AI will reach its potential when it is embodied. AGI is not necessary for an AI revolution if we get affordable versatile robotics. (added in 2025)
“Thoughts & Links” are posts mixing topics on my mind and interesting things I have read recently. Less thought out than regular posts.
A post about our job by Éric, from last February (in French). He shares a lot of my thoughts on the topic. Here is a (translated) selection.
I fear a real crisis in our trade in a few years. Some — many — will be left on the sidelines.
If you are very tech-minded, you should do math, data manipulation, statistics and generally AI. It will be harder and require more advanced competencies than being a developer, but there will be jobs. If you want to create, for me the future lies in product-related roles, product management with a technical emphasis and interest. It means caring about the business, market, customers, etc.
For those of you still mostly writing code, I would switch to LLMs ASAP. I know you don’t feel the need, that those tools make mistakes you don’t, that today it slows you down. […] There will be room for older code experts for a long time, in maintenance and large companies that are several generations behind. We still need COBOL developers today. But is it really the position you aspire to?
A post by Sean Goedecke about how strong engineers break rules and get away with it. (On a side note, I am impressed by how Sean manages to publish interesting content so frequently.)
Like I said on Bluesky it is a very important point to learn mid-career. The caveat is that most people will take it too far at some point. You should do it anyway — you need to learn exactly the right balance and nobody will teach you.
Sean (again!) has written a post about what kind of work he wants. Actually, he did it twice; this is an update from his first post back in 2021. I think this is a very interesting exercise and I should do it sometime. (It is hard for me because I have conflicting interests so I keep oscillating between technically exciting, product-focused roles and ones that are more business- and customer-facing.)
My favorites:
- Writing code is the hardest part of software development
- A system can be maintained without being understood
Some I am not sure about:
- LLMs will become reliable decision makers
- Users will tolerate bad quality
For the first one, I do not think anyone on Earth is competent to guess what LLMs or whatever will replace them will be able to do or not in a few years. People who say they will never be able to do something typically have either a very shallow understanding or a restrictive (and irrelevant) definition of LLMs.
For the second one, I sadly know from experience that users do tolerate bad quality. Not all users, but often enough for the worse alternative to win.
A long-form post on X (that should be a blog post…) about something every engineer involved in a sales process — or talking to customers in any capacity — needs to learn.
Interacting with customers as an engineer is very different and sometimes contradicts what an engineer working on a product does. I love doing both — and I think it is important that some people do both — but it is always going to be hard and feel a bit schizophrenic.
A post by Simon Tatham. His policy is:
Things should either be deliberately permanent in an organized way, or strictly temporary.
I do the same. The first thing I do when I log onto a new machine is disable persistent shell history, I turn my computer off every night and configure my browser not to remember tabs.
I really liked that post because I have been doing this forever — and I know most people do not — but I never really thought about why or formalized it. In my case it was initially because I used a lot of different and often shared machines, but now I just prefer it that way. I guess it is somewhat similar to how some cooks are obsessed with clearing the worktop as soon as they can.
A (sub)post by Karl Dubost about copyright and that message posted by someone else on Bluesky:
20 years ago we were suing teenagers for millions of dollars because they were torrenting a single Metallica album and now billionaires are demanding the free right to every work in history, so they can re-sell it. The law only ever serves capital.
Karl writes (translated from French):
Sam Altman was born on 22 April 1985, so he is 40. BitTorrent came out in 2001, 24 years ago. […] Sam Altman was 16. So he’s only doing now what he was doing when he was a teenager. My answer: teenagers from that era have grown up and some became CEOs. There’s no mystery.
2001 was the year I really went online and started getting involved with the F/OSS movement. I am from the same generation as Altman, the BitTorrent (and Napster) generation, the Aaron Swartz generation, and I will always be against policies that prevent copying and sharing digital content.
On the same topic, Karl also wrote here:
I am a creator. I constantly steal others’ work. It is the nature of creation. We do not live in a vacuum. We imitate life around us. Babies, kids do that. We keep doing that our whole life. We do not ask consent from all those who have created something before us to create ourselves. We train on everything we see. Stealing is not the problem. […] Consent is not the problem. The main problem is ownership and the ability of certain actors to capture that ownership and prevent others from enjoying it.
(On a side note: I do not enjoy translating Karl. I really enjoy his style in French; I always feel like I do not do him justice.)
Armin Ronacher writes:
Rob Pike famously described Go as suitable for developers who aren’t equipped to handle a complex language. Substitute “developers” with “agents” and it perfectly captures why Go’s simplicity benefits agentic coding.
But I think the most important part is the structural typing:
Interfaces in Go are structural. If a type has the methods an interface expects, then it conforms. This is incredibly easy for LLMs to “understand”. There is very little surprise for the agent.
Other important points are speed (both of execution and compilation) and low ecosystem churn.
A year ago, this text came out in Phrack. It is about how we need hackers more than ever to keep understanding the complex and ever-changing systems AI unleashes on us. (Go read it now and come back!)
A big reason I left the AI field and the end of 2013 is I did not like deep learning. I was a proponent of simple, sharp tools in the Unix tradition. I wanted to understand things entirely despite the complexity, like I did with distributed systems. I said “I am a physicist, not a biologist”.
I came back in 2022 because AI is there to stay. It will run the world, and we will need hackers to poke at both its physics and biology. A lot has changed since 2013; interpretability research is a thing now, and we have had tools to build something we understand from something we do not understand for a long time.
So I am here to stay and understand as much of it all as my feeble human brain lets me.
On a related note, I liked that on Henry Ko’s About page:
I am interested in ideas that share a theme of “doing more with less”.
He is talking about less energy, less data… and I would add less architectural complexity. That’s how we hackers can help.
Every program is just scaffolding for your next understanding. […] The code isn’t just temporary because we’ll rewrite it. It’s temporary because our understanding keeps evolving. […] Your current understanding is probably wrong. Build simple scaffolding that lets you discover why.
One way to understand why AI-assisted coding is so powerful is to see it as an aerial lift. Sometimes you can get to a better understanding in seconds without bothering with all the scaffolding.
There has been movement in the field, including in France, from Hugging Face’s LeRobot to the founding of Genesis AI.
That is something I am really excited about. I mean come on, we can live in Star Wars! More seriously, automating physical jobs and tasks is still one of the top things that could change our lives. It will disrupt them too, sure, but I am optimistic about the end result on this one.
When asked “What trick of the trade took you too long to learn?”, Hillel Wayne answered:
Shorten your feedback loop. No, shorter than that.
This is such a good answer, and one that I have agreed with forever (I mean, I have been using Lua for 20 years…) that I have added it to my core principles.
Note: Yes I use em-dashes when I write, and no this is not AI. When I blog I typically only use Harper to check for spelling and syntax.
I have talked about my use of AI to write code here, but that was almost two years ago. Things have changed since then and I wanted to talk briefly about how.
The most important change is that I now use Claude Code. I don’t use it all the time, but here are a few ways I do:
Here is one way I do not use Claude: when I work on hard problems where I do not know the solution. I use code as a thinking tool, and using an LLM disturbs that process.
In general, I see Claude as a junior but very fast pair programmer. It won’t help with hard things but it will gain you a lot of time for the simple ones if you guide it well and check its work.
Another tool that I used a lot is Lovable, and I have a complicated relationship with it. The way I used it was, someone else vibe-coded an app and I stepped in when things became too complex for Lovable.
At first I was very impressed. In a few prompts anyone can get a usable application prototype, all in the browser. This makes most no-code frontend development tools irrelevant.
The more I used it, though, the more I saw its limits. The recently released agent mode makes it better, but it still has flaws such as writing spaghetti code and not cleaning up dead code. As time progresses, its context becomes polluted by the messy codebase and it gets lost. I think the stack they chose (React and Radix) does not help with that.
The way to fix it is what I did: clone the repository, refactor by hand or with the help of Claude Code, and put things back in a state where the model will start making the right decisions again.
But the main issue with Lovable is not the model, it is everything around it. It uses Supabase as its only backend, which is very limited. Most importantly there is no proper way to have separate environments, not even a development environment separate from production. They need to fix that and understand that if they want to have successful applications in production they must consider the fact that they will be worked on by teams.
I still use GitHub Copilot for autocomplete. It is just better than the default, especially in Sublime Text.
I also use Mistral’s Le Chat for random things, like most people use ChatGPT.
I have not yet tried alternatives to Claude Code except for Aider which I used earlier. I am most interested in Amp for looking well-made, opencode for being Open Source and gemini-cli for being basically free (as in beer).
I think in a few months to years more asynchronous tools such as Devin, OpenAI’s Codex or Google’s Jules may become interesting, but they are not for me just now.
If you are looking for content elsewhere to make up your mind, I think this post by Simon Willison is very relevant, and I like what David and Armin post.
The rapid evolution of AI makes me change my mind often regarding its future evolution. However, since the beginning of the deep learning era, I hold a belief that in the long run the distinction between “training” and “inference” will fade and AI will learn online.
I think economics make it unlikely that the current practice of collecting data then training a model on it and doing it all over again for the next iteration will subside. I find it more likely that at some point all of the following will happen:
This vision is largely coherent with Google’s Pathways, and I suppose other large players are seeing the same thing.
“Thoughts & Links” are posts mixing topics on my mind and interesting things I have read recently. Less thought out than regular posts.
Mostly links today. I have renamed the post series “Thoughts & Links” since this is what they are.
If you know, you know. If you don’t, picture this: it’s the second tech boom (early 2010s), most people did not experience the dot-com bubble and are figuring this stuff out, and some of the best advice you can find online comes from a robot dinosaur that writes in all caps. You can find some of its content online, for instance on Fred Wilson’s blog.
In technology, once you have bad programmers, you’re doomed. I can’t think of an instance where a company has sunk into technical mediocrity and recovered.
In the early days Facebook made a point of hiring programmers even for jobs that would not ordinarily consist of programming, like HR and marketing.
Hacker culture often seems kind of irresponsible. That’s why people proposing to destroy it use phrases like “adult supervision.” […] But there are worse things than seeming irresponsible. Losing, for example.
This is a tribune by Quentin Adam on AI and industrial policy in the EU. Excerpt translated from French:
We need companies led by technology leaders, trained engineers who deeply understand the challenges of the sector. […] It is time to stop only investing in managers and to fund those who know how to build.
Philippe Silberzahn writes on how France can do great things, but 1) to preserve the past and not build the future and 2) only by circumventing legislation.
Translated from French (there is a pun in the original I could’t find a good translation for):
I meet a lot of [public and private sector leaders], they all have the same lament: we are suffocating under millimetric regulations. The country belongs to inspectors who spun out of control. Excellence has become impossible.
Philippe Silberzahn again, translated from French again.
Management without exigence is inhuman. How many talented engineers […] languish in companies where nothing ever happens except for a continuous flow of bureaucratic events? […] Accepting the mediocrity of an employee is disrespectful. They are invisibilized: “You are doing a bad job but I won’t tell you, and even less demand you improve.” It means you’re not worth it, I won’t help you produce work you can be proud of. […] And collectively, we will make crap, or even better: we will make nothing at all. And we will die slowly.
Upspin, like Unix and Plan 9, was intended to foster communities of sharing, but has been less successful at that than we hoped. As a consequence, with regret, we have decided to turn down the central infrastructure such as the keyserver over the coming months.
Last December I bookmarked this:
Nvidia’s not the only one that can increase memory capacity of course. ASICs can do this and in fact AMD may be well positioned due to their higher memory capacity versus Nvidia […] Well except Santa Huang has a Red-Nosed Reindeer called NVLink. No other accelerator in the world has all-to-all switched connectivity. No other accelerator in the world can do all reduce through a switch.
Then DeepSeek proved this was not the moat we thought it was. Meanwhile Mistral is doing over 1000 tokens per second on a 123B model by leveraging Cerebras for inference, and Tenstorrent is shipping… fun times!
You are a machine learning algorithm. You have some priors in your DNA. You learn on data, RL style because your actions affect the next data you see; the dataset depends on the model. […] How much of [Christianity’s decline] is people starting to figure this out?
The author of htmx on some “dirty” code practices he considers good:
- (Some) big functions are good, actually
- Prefer integration tests to unit tests
- Keep your class/interface/concept count down
Avery Pennarun, founder of Tailscale, on X:
Engineers (who aren’t kept isolated in a back room) are pretty good at knowing what’s important to work on. They just don’t think they are. And they especially don’t think other engineers are.
I answered:
And French engineers are probably even worse… You have no idea how many think you need a business degree to understand a customer, or anything remotely related to strategy.
He answered:
I keep sensing the “people live up (or down) to the expectations you put on them” effect. If you expect people to have bad ideas, they won’t be careful. If you tell people they get to choose what’s important, there’s no safety net, they’ll live up to what’s needed.
Insightful.
A long article by Nabeel S. Qureshi on his experience at Palantir. The most interesting part for me is how Palantir uses Forward-Deployed Engineers differently from most companies.
[FDEs] gain intricate knowledge of business processes in difficult industries (manufacturing, healthcare, intel, aerospace, etc.) and then use that knowledge to design software that actually solves the problem. [Product Development] engineers then “productize” what the FDEs build, and — more generally — build software that provides leverage for the FDEs to do their work better and faster.
I like how DHH increasingly focuses on single-developer teams. On X:
I view everything I work on through that One Developer Framework lens. Not interested in tooling, approaches, or concepts that demand a team of thinly-sliced specialists to tick. One developer must be able to understand/use it all. From Hello World to IPO! Let’s go, nerds :)
Howard Chu on Mastodon:
A lot of user interactions these days are too user friendly, and when deployed in the real world you discover that you really need a bit of friction, to prevent things from being too easy. Child-proof packaging on medicines is one example where you purposefully raise difficulty. […] Stop trying to make everything “so easy a child can do it.” Some things need to have a higher barrier to entry. Focus on educating people to be smart enough and responsible enough to clear the hurdles.
We won because we started at the right time and we had taste. We were there when a new paradigm was being born and we approached the problem of helping people embrace that new paradigm with a developer experience centric approach that nobody else had the capacity for or interest in.
On YouTube, translated from French:
As you progress, you must be able to analyze a situation more and more remote from pure technology, and it is interesting to do so because you will also need to understand marketing, sales, finance, and so on. No company that does not have technical people involved with business strategy can have a tech or product approach anyway. All those companies I hear complain like: “We do not innovate enough! We hired the best developers but can’t see a significant impact…” Systematically, we notice no tech person is working on strategy. There is no tech person at product meetings, no tech person at executive committees, etc.
On YouTube, translated from French:
At a startup, what I call “human capital” is rare and limited. You cannot hire armies of people, so you need rare birds able to do several things well, or at least people with the potential to become them.
The literal translation of what he says in French is “you need five-legged sheep”, I couldn’t find a good translation in English.
There is an Ivan Illich quote that I often see in French, for instance in Pierre Pezziardi’s LinkedIn banner:
L’outil simple, pauvre, transparent est un humble serviteur ; l’outil élaboré, complexe, secret est un maître arrogant.
Here is how I would translate it into English:
Simple, plain, transparent tools are but humble servants. Elaborate, complex, secret tools are arrogant masters.
But this quote comes from La Convivialité, the French translation of Tools for Conviviality, so why translate when we can find the original?
In the French book, the quote is the last sentence of the Overprogramming chapter, which ends like this:
Au fur et à mesure que des outils post-industriels rationnels se répandront, les tabous du spécialiste suivront l’outillage industriel dans sa chute, comme ils l’avaient accompagné dans sa gloire. L’outil simple, pauvre, transparent est un humble serviteur ; l’outil élaboré, complexe, secret est un maître arrogant.
The original English version, however, is this:
Professional taboos and industrial tools stand and fall together once truly rational, postindustrial tools are available. Only the convergent use of convivial tools in all significant areas of need-satisfaction can render their use in each sector truly effective. Only among convivially structured tools can people learn to use the new levels of power that modern technology can incorporate in them.
This is completely different! Which means this quote was probably from Luce Giard et Vincent Bardet, who helped Illich with the French version of his book.
I have known about this for a while but I decided to blog about it because of a discussion I had at FOSDEM about LLMs replacing human translators. And indeed for most content LLMs do a good job, but it is unlikely that an LLM would do something like this. This is not what they are trained to do. There is an opinionated, artistic component to the job of literary translation.
Note: Karl Dubost also frequently blogs about this topic.
I am attending FOSDEM in Brussels for the 13th time physically. This is an event I anticipate every year, where I get to meet old friends and new people, watch interesting talks, and drink some Belgian beer.
This year I spent some time with Gawen, Denis, Hisham, my brother François, and a few other current and former colleagues.
Here is a list of talks I saw:
I also sometimes use the opportunity to write a little code, this year I fiddled with aiomonitor and async generators in Python.
This is just a short post to describe how I use the Teal language server. My setup is a bit specific: I use Sublime Text on Arch Linux and I often run development branches of Teal so I run the LSP from source. I will assume you put it in /.../git.
I also use localua to manage all my Lua / Teal environments and I have .lua/bin in my PATH (which means that luarocks, lua and tl point to the local versions in my shell).
Install tree-sitter-cli which is a system dependency, clone the repository somewhere, initialize a local Lua environment and install the dependencies with LuaRocks:
sudo pacman -S tree-sitter-cli
cd /.../git
git clone [email protected]:teal-language/teal-language-server.git
curl https://loadk.com/localua.sh -O
sh localua.sh .lua
luarocks make
luarocks install tlcheck
Run teal-language-server --help to check it works.
Open the LSP settings (Preferences > Package Settings > LSP > Settings) and configure Teal like this:
{
"clients": {
"teal": {
"enabled": true,
"command": [
"/.../git/teal-language-server/.lua/bin/teal-language-server"
],
"selector": "source.teal"
}
}
}
tlconfig.lua file to your projectIf you do not use this step, the LSP will only lookup dependencies in its own Lua environment. You want it to look in your project instead.
The contents of tlconfig.lua can look like this:
return {
source_dir = ".",
include_dir = { ".", ".lua/share/lua/5.4" },
gen_compat = "off",
gen_target = "5.3",
}
The important part is .lua/share/lua/5.4 in include_dir. For the rest, settings depend on your project.
Note that even if you use Lua 5.4 gen_target must be 5.3, otherwise the LSP will not work currently even if you specify gen_compat = "off".
I started my professional career almost 15 years ago working for a Machine Learning / Computer Vision company that did image recognition on mobile. At the end of 2013, I chose to leave it for Lima, in part because even at Moodstocks I was always more of a Distributed Systems / Algorithms person than a Deep Learning person.
Now, after 10 years away from the field, I am back working on AI for images, this time more specifically on automated and realistic image edition, with a part of the old Moodstocks team who spent years at Google after the acquisition.
I did not make that choice lightly, I spent some time pondering it, because I knew it would require a significant time investment. AI is a complex, fast-moving field where not everything you learned elsewhere applies. Now that I am back, it is unlikely that I will leave it anytime soon.
I got several interesting job offers recently, for Staff+ roles at great companies with classical distributed systems problems that would have appealed to me a few years ago. But it is likely that the real dist.sys fun in the next few years will be in AI, both for training and inference. The field has grown fast, and the tooling and practices could not keep up so there is a lot to build.
Those who know me may also remember I always had a problem with the issue of interpretability / explainability of deep learning models, but in recent years I feel we have collectively improved on that, although there is still much to be done.
In addition to that, I do not intend to leave Finegrain specifically either! We have a great team and we approach problems differently from most AI companies. We have products starting to ship, and a lot to do to improve them. I don’t think I should be anywhere else.
Anyway, I am taking the end of the week off to attend and help with the dotAI conference, and I will be back to work next Monday. In the meantime, try the Finegrain Editor if you haven’t already :)
“Thoughts” are posts about what has been on my mind. Sometimes practical, sometimes not; often just things I read recently. Less thought out than regular posts.
I have not made a “Thoughts” post in 4.5 months so I had a lot of topics in store. I have decided to focus on “soft” topics only this time, so if you are only looking for technical content you can skip this post.
In 1959, Bertrand Russell was asked on BBC what would be worth telling later generations.
His answer was divided into two parts, intellectual and moral. The latter, I think, may be of some use today.
Love is wise, hatred is foolish.
In this world, which is getting more and more closely interconnected, we have to learn to tolerate each other. We have to learn to put up with the fact that some people say things that we don’t like.
We can only live together in that way. And if we are to live together and not die together, we must learn a kind of charity and a kind of tolerance which is absolutely vital to the continuation of human life on this planet.
We tend not to do that. We take sides arbitrarily, probably more easily online than offline. We create fractures that make participation in any community more tiring than it should be. Albert Wenger puts it like this:
The world is continuing to descend back into tribalism. […] Words have been rendered devoid of meaning and reduced to pledges of allegiance to a tribe. […]
I don’t belong to any of the tribes nor would I want to. But the effort required to maintain internally consistent and intellectually honest positions in such an environment is daunting. And it often seems futile.
I generally avoid that topic online, but I will make an exception.
Brain chemistry is complicated, and mental health is a multi-dimensional spectrum. We vaguely mark regions of that spectrum and give them disorder names; it is a necessarily simplified model. The frontiers of those regions are blurry and differ between cultures and over time. Two decades ago, for instance, behavior considered normal for kids in France would often have resulted in medication for ADHD in the US.
Today the number of diagnoses and self-diagnoses is clearly exploding. Part of the reason is that influencers, sometimes paid by medical companies, have made neurodivergence “trendy”: people feel like having a disorder makes them belong to a community. It is even worse for kids, with parents “diagnosing” them and managing to find medical professionals to confirm those diagnoses.
The problem is that studies have shown that believing that you have a mental disorder when you do not can make you exhibit the same symptoms. However they do not come from the same causes, so medication will not work beyond the placebo effect, but it will mess with your brain chemistry.
A psychiatrist once told me: “If we can avoid treating something like a disease, we should.” I think this is the right approach. We must go back to considering more zones of the spectrum “normal”.
Note that I am not saying you should not go to therapy! If anything learning CBT basics could benefit everyone. But we should normalize therapy instead of labeling everyone with a mental disorder.
Although I have been thinking about this topic for a long time, posting about it here was prompted first by a post by DHH, and more recently by a video in French where I learned about the influence of medical companies like Done and Cerebral. I also have a book about how the American approach to mental health impacts the rest of the world on my reading list.
DHH (again) explains very well why I do not use languages like Rust or C++. (*)
There is no universal set of trade-offs that’ll make something objectively “work best”. Half the programming conundrum lies in connecting to an enduring source of motivation. I wouldn’t be a happy camper if I had to spend my days programming Rust (but I LOVE so many of the tools coming out of that community by people who DO enjoy just that).
I work mostly with Python nowadays, it is not my favorite language but I tolerate it. Tools written in Rust are making my life much better, yet I would not pick Rust if I had to write such tools, even if it may be the best tool for that job.
Beginners often ask me what programming language or algorithms they should learn, and I tell them it does not matter at this stage: they should learn what motivates them the most. The older I get, the more I think this remains somewhat true your whole career.
Programmers need slack and fun projects, or their output degrades real fast. Young people with no kids can compensate for boring work with side projects, but as you get older you must make sure you find some fun at work. Jason Cohen talks about procrastinating for success.
Finally, sometimes you’ve got to let yourself do fun stuff at the expense of priorities. Most of what you do in a little company isn’t what you enjoy doing; it’s easy to become frustrated and burned-out. So sometimes you need to allow yourself to recover with plain fun. Whatever the cost of putting off duties, burn-out is even more costly.
I would add a little nuance to this: it is perfectly possible that, at some point in your life, your work will motivate you so much that you won’t want to do anything else. And that is perfectly fine, you just have to recognize when that is no longer the case.
Anyway, happy people become successful, not the other way around. There is a book about this. We are all different, so figure out what you need to be happy now, and go get it.
(*) The reason I do not like C++ or Rust is not because they are efficient, statically-typed programming languages, it is the complexity. I did write a lot of C and I am enthusiastic about Zig, for instance.
If you prefer, the code for this article is also available in a gist.
GraphQL is great because it lets consumers define the schema of their requests, however that makes it somehow harder to optimize than typical REST requests. If you implement GraphQL navively, you will quickly get the infamous N+1 issue. Let me demonstrate the issue with an example project using Quart, Quart-DB, PostgreSQL and Strawberry.
Let us start by defining a simple schema for a music collection and populating the database with a migration:
from quart_db import Connection
async def create_schema(cnx: Connection) -> None:
await cnx.execute(
"""
CREATE TABLE bands (
id bigint PRIMARY KEY,
name text NOT NULL
);
CREATE TABLE albums (
id bigint PRIMARY KEY,
name text NOT NULL,
band_id bigint REFERENCES bands(id)
);
CREATE TABLE songs (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name text NOT NULL,
album_id bigint REFERENCES albums(id)
);
"""
)
async def populate(cnx: Connection) -> None:
await cnx.execute(
"""
INSERT INTO bands (id, name) VALUES
(1, 'Dark Tranquillity'),
(2, 'Pineapple Thief'),
(3, 'Wintersun');
INSERT INTO albums (id, name, band_id) VALUES
(1, 'Haven', 1),
(2, 'Fiction', 1),
(3, 'Atoma', 1),
(4, 'Time I', 3),
(5, 'Your Wilderness', 2),
(6, 'Versions of the Truth', 2);
INSERT INTO songs (name, album_id) VALUES
('The Wonders at Your Feet', 1),
('Not Built to Last', 1),
('Indifferent Suns', 1),
('At Loss for Words', 1),
('Terminus', 2),
('Inside the Particle Storm', 2),
('Focus Shift', 2),
('Forward Momentum', 3),
('Caves and Embers', 3),
('When Mountains Fall', 4),
('Sons of Winter and Stars', 4),
('Land of Snow and Sorrow', 4),
('Time', 4),
('The Final Thing on My Mind', 5),
('Tear You Up', 5);
"""
)
async def migrate(cnx: Connection) -> None:
await create_schema(cnx)
await populate(cnx)
We expose bands, albums and songs; now let us build a GraphQL interface to query them the naive way:
import logging
from typing import Any
import strawberry
from quart_cors import cors
from quart_db import QuartDB
from strawberry.quart.views import GraphQLView as QuartGraphQLView
from quart import Quart, Request, Response
app = Quart("sfdl")
app.logger.setLevel(logging.INFO)
app.config["QUART_DB_DATABASE_URL"] = "postgresql://postgres@localhost/cwl_sfdl"
app.config["QUART_DB_AUTO_REQUEST_CONNECTION"] = False
db = QuartDB(app)
cors(app, allow_origin="*", allow_methods=["GET", "POST"])
@strawberry.type
class Song:
id: int
name: str
album_id: int
@strawberry.type
class Album:
id: int
name: str
band_id: int
@strawberry.field
async def songs(self) -> list[Song]:
query = """
SELECT id, name, album_id
FROM songs
WHERE album_id = :album_id
"""
async with db.connection() as cnx:
result = await cnx.fetch_all(query, {"album_id": self.id})
songs = [Song(**row) for row in result]
app.logger.info(f"Got {len(songs)} songs.")
return songs
@strawberry.type
class Band:
id: int
name: str
@strawberry.field
async def albums(self) -> list[Album]:
query = """
SELECT id, name, band_id
FROM albums
WHERE band_id = :band_id
"""
async with db.connection() as cnx:
result = await cnx.fetch_all(query, {"band_id": self.id})
albums = [Album(**row) for row in result]
app.logger.info(f"Got {len(albums)} albums.")
return albums
@strawberry.type
class Query:
@strawberry.field
async def bands(self) -> list[Band]:
query = """
SELECT id, name
FROM bands
"""
async with db.connection() as cnx:
result = await cnx.fetch_all(query)
bands = [Band(**row) for row in result]
app.logger.info(f"Got {len(bands)} bands.")
return bands
class GraphQLView(QuartGraphQLView):
async def get_context(self, request: Request, response: Response) -> dict[str, Any]:
return {"request": request, "response": response}
view = GraphQLView.as_view(
"graphql_view",
schema=strawberry.Schema(query=Query),
graphql_ide="graphiql",
)
app.add_url_rule("/", view_func=view)
We can try it with GraphiQL, to check that it works:

It does, but if we look at the logs we see this:
INFO in __init__: Got 3 bands.
INFO in __init__: Got 2 albums.
INFO in __init__: Got 1 albums.
INFO in __init__: Got 3 albums.
INFO in __init__: Got 2 songs.
INFO in __init__: Got 4 songs.
INFO in __init__: Got 0 songs.
INFO in __init__: Got 4 songs.
INFO in __init__: Got 2 songs.
INFO in __init__: Got 3 songs.
[59520] [INFO] 127.0.0.1:52944 POST / 1.1 200 806 5311
It does too many requests: first it gets all the artists, then for each artist it gets the albums, then for each album it gets the songs… That is the N+1 problem.
If you look it up online, you will quickly find out that a popular solution is dataloaders. However, a lot of examples you will find do not correspond exactly to that problem, because they only demonstrate how to use dataloaders on primary keys, whereas here we want to load on a foreign key. This is the case of the official Strawberry documentation, which you should still read too if you use that library.
The important thing to understand with dataloaders is that they take a list of keys and return a list of answers of the same size, correponding to those keys. So you cannot, for instance, have a dataloader that takes a list of album IDs and returns a list of all the songs in those albums. However, the trick is that you can have a dataloader that returns a list of lists of songs corresponding to each album!
Now let us rewrite our example with that pattern:
import logging
from collections import defaultdict
from functools import cached_property
from typing import Any
import strawberry
from quart_cors import cors
from quart_db import QuartDB
from strawberry.dataloader import DataLoader
from strawberry.quart.views import GraphQLView as QuartGraphQLView
from strawberry.types import Info
from quart import Quart, Request, Response
app = Quart("sfdl")
app.logger.setLevel(logging.INFO)
app.config["QUART_DB_DATABASE_URL"] = "postgresql://postgres@localhost/cwl_sfdl"
app.config["QUART_DB_AUTO_REQUEST_CONNECTION"] = False
db = QuartDB(app)
cors(app, allow_origin="*", allow_methods=["GET", "POST"])
@strawberry.type
class Song:
id: int
name: str
album_id: int
@strawberry.type
class Album:
id: int
name: str
band_id: int
@strawberry.field
async def songs(self, info: Info) -> list[Song]:
dl = info.context["dataloaders"].songs_for_albums
return await dl.load(self.id)
@strawberry.type
class Band:
id: int
name: str
@strawberry.field
async def albums(self, info: Info) -> list[Album]:
dl = info.context["dataloaders"].albums_for_bands
return await dl.load(self.id)
@strawberry.type
class Query:
@strawberry.field
async def bands(self) -> list[Band]:
query = """
SELECT id, name
FROM bands
"""
async with db.connection() as cnx:
result = await cnx.fetch_all(query)
bands = [Band(**row) for row in result]
app.logger.info(f"Got {len(bands)} bands.")
return bands
class DataLoaders:
@staticmethod
async def load_songs_for_albums(keys: list[int]) -> list[list[Song]]:
query = """
SELECT id, name, album_id
FROM songs
WHERE album_id = ANY(:keys)
"""
async with db.connection() as cnx:
result = await cnx.fetch_all(query, {"keys": keys})
songs = [Song(**row) for row in result]
app.logger.info(f"Got {len(songs)} songs.")
by_key: defaultdict[int, list[Song]] = defaultdict(list)
for song in songs:
by_key[song.album_id].append(song)
return [by_key[k] for k in keys]
@staticmethod
async def load_albums_for_bands(keys: list[int]) -> list[list[Album]]:
query = """
SELECT id, name, band_id
FROM albums
WHERE band_id = ANY(:keys)
"""
async with db.connection() as cnx:
result = await cnx.fetch_all(query, {"keys": keys})
albums = [Album(**row) for row in result]
app.logger.info(f"Got {len(albums)} albums.")
by_key: defaultdict[int, list[Album]] = defaultdict(list)
for album in albums:
by_key[album.band_id].append(album)
return [by_key[k] for k in keys]
@cached_property
def songs_for_albums(self) -> DataLoader[int, list[Song]]:
return DataLoader(self.load_songs_for_albums)
@cached_property
def albums_for_bands(self) -> DataLoader[int, list[Album]]:
return DataLoader(self.load_albums_for_bands)
class GraphQLView(QuartGraphQLView):
async def get_context(self, request: Request, response: Response) -> dict[str, Any]:
return {"request": request, "response": response, "dataloaders": DataLoaders()}
view = GraphQLView.as_view(
"graphql_view",
schema=strawberry.Schema(query=Query),
graphql_ide="graphiql",
)
app.add_url_rule("/", view_func=view)
The request for the whole collection still works, but now if we look at the logs we can see this:
INFO in __init__: Got 3 bands.
INFO in __init__: Got 6 albums.
INFO in __init__: Got 15 songs.
[59520] [INFO] 127.0.0.1:60244 POST / 1.1 200 806 4509
No more N+1, our job is done here.
“Thoughts” are posts about what has been on my mind. Sometimes practical, sometimes not; often just things I read recently. Less thought out than regular posts.
It is always interesting to compare those two companies. They differ in their fundamental principles and goals, the way they operate, their technical stacks…
The mother of all comparisons is Steve Yegge’s famous rant. More recently, I have also really enjoyed posts by Carlos Arguelles who went from Amazon to Google and (very recently) back.
In the disaggregated write-ahead log, Shikhar Bhushan highlights an important difference between the two giants’ architectures. At Google the distributed filesystem (Colossus, formerly GFS) is a fundamental building block. It is not global but zonal, and replication is built on top of it. At AWS the globally distributed log — which is not exposed as a public service — is a fundamental primitive, and S3 is built on top of it. Shikhar is now building that service.
Jaana Dogan also recently tweeted about another difference:
At Google, a lot of time is spent on rewriting infra or migrating to new infra. AWS rarely kills its services, hence if there are no new hard problems due to growing scale or something, it’s fairly cheap to keep the lights on.
That would be part of the explanation for why Google often kills its services when Amazon maintains them. Although I think Amazon’s customer obsession is crucial too.
I have been re-reading some older articles by Philippe Silberzahn (in French) and this one deserves a mention.
He describes how many people have a view that “Big Problems” such as climate change can only be solved with radical actions. He posits that it stems from at least six mental models:
His view is that Big Problems often have a large social component, and that the large change required to solve them does not usually come from radicality but from small actions carried out by people with alternative mental models.
Note: I am expanding on something I posted elsewhere here.
I think I finally understood a problem I have with many type systems added on top of dynamic languages. In practice, they work against the I and D of SOLID. You can make Interface Segregation work, but you need granular interfaces and intersection types. As for Dependency Inversion, typing makes boundary code very verbose, and using Dependency Inversion reduces the advantages of using typed libraries anyway, since if you do it well library types should not leak from the boundary into client code.
It is also interesting to note that AI appears to be bad at writing code with inverted control. It shows it “reasons bottom-up”, which makes sense when you know how it works. So the end result of typing + copilot is that I write code that is less flexible and that requires larger diffs to change. It is not necessarily harder to change, because the tooling helps.
I have a feeling it is not always good or bad, it probably depends on the kind of code I write. SOLID is not absolute. But it is annoying me that things that I still view as “mere tools” introduce so much friction to achieve patterns that I consider elegant and useful.
On a related note, Armin Ronacher — who mostly switched to Rust nowadays — has an interesting blog post about his view of typed Python too.
Daniel Mangum thinks the most important skill in startup engineering leadership is pacing. I agree wholeheartedly. Pacing in startups is hard to get right: it depends on many forces, it is often a tradeoff, and getting it wrong can be disastrous.
Phil Eaton — who recently changed jobs — wrote a very good post about what to do when you join a new company or team in a senior position. He also mentions internal blogs, which is something I rarely see but always thought was worth doing since I read about it in The Year Without Pants.
In Both pyramids are white, Vicki Boykis talks about a specific kind of groupthink. Unlike the pyramids, it is not black or white, there is a tradeoff between dissenting when necessary and not dissenting when not. We all have a natural tendency for one or the other, that we need to acknowledge and fight to find the right balance.
Dave Paola explains why non-DRY specs are more maintainable. There was no need to convince me on this. He quotes Richard Gabriel:
The primary feature for easy maintenance is locality. Locality is that characteristic of source code that enables a programmer to understand that source by looking at only a small portion of it.
Speaking of Richard Gabriel, I have read a few articles about Rust and Zig recently. The more I do the more I think it may be a Worse is Better situation in modern systems software. It has never been clear if one is actually better. The Better (Rust) side seems to be winning market share for now, but personally I am more drawn to Zig.
Tom Tunguz thinks databases won’t charge for storage in the future. The pendulum keeps swinging between colocating compute with data and separating them. Both have advantages and drawbacks. I have successfully argued for colocation in several cases (“bringing compute to the data”), but it is true that for some categories of systems they tend to be decoupling successfully today. Quickwit is a great example.
Finally, Justin Jaffrey points to cases where compositionality fails. We love clean abstractions that do not leak, but when you get into the nitty-gritty details of some kinds of software, you cannot realistically have them. Justin mentions distributed systems and query planners, and this also makes me think about compilation stacks. You might think there is a nice layered model, but when you look at the details it is often far from being the case.
“Thoughts” are posts about what has been on my mind. Sometimes practical, sometimes not; often just things I read recently. Less thought out than regular posts.
Matt Rickard talking about the fifth step of Elon Musk’s manufacturing algorithm. Automating too early is one of the mistakes I have seen the most in software companies. Once people see the benefits of automation, they tend to abuse it. Doing that results in poor comprehension of the system and friction when changes are needed.
All of the steps of Musk’s algorithm are relevant to software too, as is the fact that they are in chronological order. I won’t copy/paste them here, go read the end of Matt’s post. As far as I know, its source is Walter Isaacson’s book.
Matt (again) on how the way we see Generative AI today is only a temporary state. In most cases, raw models will not be exposed to users. We will often want AI use to be more subtle, hidden in the background even. To achieve that we will need a better, more deterministic and flexible interface around the models. This is why I predict a project such as Outlines or LQML is likely to become very important soon.
In his Rails World keynote, DHH talked about how the low interest rates we had for over a decade were a likely cause of the success of complex, over-engineered tools and the hyper-specialization of software jobs.
I think this may be true, and if so I hope the pendulum is swinging back.
This is a long and very interesting post. Greatness is often born of terrible situations: misery, war, disease… But then if progress can eliminate those issues, should we do it? Noah Smith argues that yes, we should, hence the subtitle:
Adversity isn’t worth the price of adversity.
You may note that this theme is somehow related to this post. The related meanings of life and progress is a topic I’m thinking about a lot at the moment.
When you ship something significant you should take some time to either ensure it will be preserved and accessible by you forever, or at least that you can remember it years later, by writing about it and taking screenshots for instance.
Note that I disagree with the “It’s never too late” part. I have not done that for some early work projects in my career, and I regret it now. Do it while you can.
The tension between Simple and Easy has always been an important point for me. Hajime Hoshi gives an interesting definition:
Easy: how quickly you can achieve what you want
Simple: how quickly you can understand the model
Easy is nice to have, Simple is crucial. The effect of “easy” is linear whereas the effect of “simple” is exponential. Never trade the latter for the former.
An article in French and paywalled, sorry. But the issue of energy generation and consumption has been on my mind for a long time, and is even more pregnant now. Also a Talos Principle 2 issue!
Regulation is probably a niche topic for this blog, but it is one I have been following for a long time — ever since I got involved in activism against software patents in the mid-2000s.
This is the best article I have found on the Cyber Resilience Act. Like most EU regulations it is not black and white. If you are interested, go read it and make up your own mind.
The main problem I have with regulation is that the effects are not always easy to predict and not properly re-evaluated. For instance, I believe several laws passed to protect EU citizens and industry against US giants are having the opposite effect. We often forget that added complexity often introduces economies of scale.
OK, there is no question that this one is a Talos topic :)
Not everything in life is a pitch to a VC. Organising human activity purely on the basis of return on investment is a dead end philosophy masquerading as rationalism.
Better just to be brave, hold your head up and say “this is what I believe”, and then apply rationality to how you should shape the universe to realise your values. A great country does great things because of greatness itself.
Some programmers just want to ship fast and don’t care about writing fragile or broken code, they will “fix it later”. It might work for your personal project, but as soon as you work with other people you’re losing everyone else’s time by doing that.
It sounds obvious, but I see people blame reviewers too often. No. The purpose of code review is to discuss design decisions, not find the bugs you wrote because you didn’t pay attention to edge cases or side effects.
Oh, and sure a few unit tests may help too! But they’re not the main point. They’re more a consequence, and a proof that you thought about things, as well as a way to prevent someone else — or future you — from breaking assumptions in the future.
A good blog post about team culture. In particular, acknowledging that some diversity is good, but that it does not mean there should be no shared culture at all within a team or a company.
The remote vs co-located work example is good. I know great fully-remote companies, and some where everyone works 100% from the office. Many of the people who love the former would hate the latter, and vice-versa. Why would you insist on joining a company that works differently from what you like and change its culture?
Similarly, there are companies where everyone is very close and others where relationships are purely professional. Why would you insist that one or the other is wrong? If you want to join a team, adapt to its culture, or find another one that fits you better.
Culture and values are not something you write on a document and mandate top-down, they’re bottom-up and organic. The best way you can shape culture if you really want to is by picking who you hire carefully. The worst thing you can do is mission someone to change a team or a company’s culture. If you ever are in a context where someone tries to do that and you cannot reason with them, there is only one thing to do: run.
This is not new, but I am using Beanstalk for Finegrain. I was already using it at Moodstocks over a decade ago. In the meantime, I have used several other queues, but Beanstalk is still my favorite. It works well and it is simple. I love tools like that.
I am using Beanstalk in a Quart application with aiostalk, but I still maintain my Lua client so maybe this will be how I sneak some Lua at yet another company this time… :)
Last weekend I attended my 12th FOSDEM, not counting the two years of “remote FOSDEM” due to COVID.
For a few years now I’ve been going less for the talks than to meet old acquaintances and make new ones. Still, I did see several interesting talks, and like 10 years ago I decided to blog about them.
Note: I saw other talks that were less interesting, I will not mention them.
I started my Saturday with a series of lightning talks.
A talk about Overte, a system similar to Second Life but distributed. Advantages: it’s much cheaper to run and users are more in control. Drawbacks: the world cannot be coherent, and it’s harder to make things reliable and durable. It cannot be censored or prevent copyright infringement by design, which is an advantage or a drawback depending on how you look at it.
Open Food Facts is 12 years old. They contributed to the success of Nutri-Score and the upcoming Eco-Score. They are expanding into other areas such as circular economy with Open Product Facts.
A few days before FOSDEM the .ru TLD was down for a lot of people (not everyone) for a few hours. Because of the context, there was wild speculation online regarding what caused it. Eventually, it looks like it was a simple technical error involving an incorrectly signed DNSSEC root key, which broke the entire TLD for people using validating resolvers.
The Linux Kernel Functional Testing project runs the same CI tests on many machines (with different architectures) simultaneously, which all download the same assets. They logically wanted to introduce a caching proxy, but transparent proxies like Squid could not do the job so they wrote their own.
My friend Loïc presented his Open Source project Azimutt, a tool to explore large database schema and data, specifically tailored to very large databases. I had not looked at Azimutt in a long time but it has become super nice. The UI looks great and there are tons of useful features to go beyond exploration and actually document the database. If you ever have to deal with a large database or explore undocumented databases like I did at Inch, you should certainly have a look at it.
Passbolt is the team password manager we used at Inch. I had seen their lightning talk last year, I saw it again this year. The project is still progressing well and I would certainly recommend it if you need to share passwords as a team — which you probably do.
Kong is using consistent hashing in the load balancer to ensure a client always gets the same backend, for cache optimization purposes. Thijs explained consistent hashing and the DNS-specific complexities of using it (this talk was in the DNS room). Someone from the audience suggested Rendezvous hashing which I’d heard about but never looked at in-depth, this prompted me to read the Wikipedia article which was interesting.
A very fun talk by an actual magician about how they share and protect their tricks. Sadly the amphitheater was far from full, probably because it was the last talk of the day. The video deserves a watch.
Daniel’s talks are always great and this one was no exception despite technical issues at the beginning. It was about his experience running the cURL project which is now old enough to drink in Europe. The content of the talk pretty much matches his online book Uncurled which I had read before and recommend.
This was a talk by a colleague from Finegrain on our Open Source micro-framework for foundation models adaptation Refiners. Obviously, you should go check it out immediately and star the GitHub repository :)
Teal, a typed dialect of Lua, is another project I use and occasionally contribute to. This year Hisham talked about upcoming features, and how and why he picked simple but somehow less powerful solutions instead of more complex alternatives.
Scott Chacon is an incredible speaker, and the FOSDEM crowd apparently knew it: the amphitheater was full and many people could not get in. The talk was a whirlwind of recent and older lesser-known Git options; watch it and you will certainly learn something.
Toward the end, Scott presented his current project GitButler, a graphical Git client leveraging virtual branches. It was recently open sourced and I tried it earlier this week, because I like the idea which could work very well with my workflow. I found it promising but still too rough around the edges to use it at work for now. I might look at the codebase someday because I am curious about the stack (Tauri and Svelte).
A talk about Pijul, a DVCS I know about but never actually used — although I did use its spiritual predecessor Darcs back in the day. It uses a very different approach from Git, representing edits as a graph that does not include the content. Having worked a lot with tree CRDTs (operation-based and state-based) and things like Interval Tree Clocks I can certainly see the advantages but also some of the drawbacks of such an approach. The project also has a fast K-V store in Rust somewhat similar to LMDB and a forge hosting its source code and offering free hosting.
FOSDEM is a bit tiring, but FOSDEM is great. Like I said, the talks sure are nice, but you should also go for the people (and the beer!). This will certainly not be my last FOSDEM, see you next year in Brussels!
Warning: this post includes some spoilers for The Talos Principle 1 and 2. If you intend to play them — and you should! — do it first and come back later.
I do not have much free time since the birth of my first son in 2020, so I have almost entirely given up video games. But I had to make an exception for The Talos Principle 2, which was released last November and which I finished earlier this week.
The Talos Principle came out in 2014, but I only played it in late 2021 and it instantly became one of my favorite games. On the surface, you play a robot solving puzzles, but it is actually a game designed to make you think, rooted in philosophy.
The first game touched on the themes of AI and consciousness, free will, religion, and the perils of progress among others. The second game expands on that last theme and adds more such as society / politics and (late game) the nature of love.
In both games, there are several layers to the story. The “main scenario” takes place in the “physical world” but a lot of content is accessed through text found in computer terminals, audio logs, text adventure games, an in-game BBS, or even inside your own subconscious. Some characters are very impactful, and yet you never interact with them otherwise than through messages they left here and there. One such character is Lifthrasir, and I cannot resist quoting some of his messages here:
When you sit in the dunes and you hear only the wind, and nothing else at all, and all the complexities of civilization fade away, you truly understand that spiritual peace is a great evil, a kind of shallow banality that we must always strive against. It is spiritual excitement and enthusiasm which sustain our humanity; spiritual peace is a vile and cowardly surrender to oblivion.
and:
That is what we do now, we seek facts rather than truth, because truth might frighten and unsettle the comfortable people who like to sit in their conference rooms and debate which corners we should cut today.
A core theme of the game is whether Humanity should face extinction risks it causes to itself (e.g. climate change) through restraint (limiting energy consumption and population growth etc) or techno-solutionism and growth (including becoming an interstellar species). Your choices about this issue influences the scenario of the game, and some characters are avatars of one way or the other as well as several middle-ground options. In the techno-solutionist camp are Byron and Melville. The latter is the engineer on your team, so for obvious reasons I can sympathize with her. A quote that resonated with me:
All we do anymore is think about the worst case scenario. You know, I didn’t even realize how much I’d limited my own imagination, how I talked myself into just accepting this incredibly poor future we’d been offered. A future where things just keep getting slightly worse every day and we accept it because we’re ashamed of ourselves. Not of something we’ve done, but just of our existence. Like we’re a virus on this planet. Like our existence is a sin.
In any case The Talos Principle 2 is, perhaps even more than the first one, a game that will make you think. It will force you to consider philosophical and ethical issues and make choices about them. You could say it’s a giant trolley problem, but that wouldn’t make it justice: it is much more than that. It is not everyday a work of fiction changes my view of some important issues, and this game did.
Oh, and the puzzles are fun, too!
If you haven’t played the Talos Principle series, I hope this post encouraged you to try it. I recommend starting with the first game if you can, since the second one has major spoilers. Personally I think you can skip the Road to Gehenna DLC if you are short on time.
Since I’m back in AI, a recurring question has been whether we are collectively automating ourselves — software engineers and other kinds of tech people — out of our jobs. So, are we? And if so, how long will it take until that becomes a problem, and is this ultimately bad? I don’t have all the answers, but here is some insight.
First, we have always tried to automate repetitive things. This is why we write libraries and frameworks to move up the ladder of abstraction — which by the way comes with its own set of challenges. We are reasonably good at it, and that is part of what justifies our high salaries, but it is clear that very small shell scripts will not be enough to replace us entirely.
In the last few years, large language models have shown somewhat impressive ability to generate code, to the point that AI replacing human programmers in the next decade has become a possibility. Like many, I have been experimenting with those tools, and my impressions are mixed.
On the one hand, I am convinced by tools that are integrated into editors and work like a better autocomplete. I have been using GitHub Copilot for a few months and it is helpful. It does not always suggest the right thing, but it does so often enough that it saves me time.
On the other hand, I am skeptical of the approach that consists of generating larger chunks of code using tools such as ChatGPT. It may work for one-shot throwaway scripts, but it is rarely helpful for writing “real” software. The reason is that it fails in two ways. The first one is generating completely wrong code (too simplistic, solving the wrong problem…), and the second one is making less obvious mistakes in the middle of code that otherwise looks good. While the former is not such a problem, the latter definitely is.
An analogy you could make is that they both work like junior programmers making mistakes, but Copilot pairs with you whereas the larger code chunks generation approach is similar to the pull request / code review workflow. If you have high-quality standards, reviewing and fixing bad code is often more time-consuming than writing it from scratch. With junior developers you do it anyway because you invest in their growth, hoping eventually they will write good code that saves you time. With current AI that learning doesn’t happen, or at least not fast enough.
Also, there is clearly a difference depending on the kind of code you write and how innovative it is. For instance, for my job at Finegrain, Copilot is very helpful with Web backend code (using Quart, SQL requests…), but much less so when I work on deep learning models using our Open Source framework Refiners. This reinforces my view that programming bootcamps that teach superficial concepts to put people on the market as early as possible are a trap; if you are a programmer and worry about your job safety, I suggest you invest in fundamentals.
If that wasn’t reason enough, AI appears to be better at solving high-level problems. By that I mean that it is good at answering questions that an average non-technical person could ask, such as “create a nature-themed landing page for my new business which sells coconut oil shampoo”, but less good at understanding the kind of problems developers can only talk about with other developers.
So it is clear to me that I will not be replaced by AI next year, but what about in 10 years? What about in 20? That is where knowing a bit about how AI works helps.
Let’s start by saying I was not surprised that code generation turned out to be an area where deep learning shines. I have been saying for over a decade that this revolution will affect white-collar jobs before blue-collar. Moreover, the current wave of AI is good at replicating things for which it has tons of examples, and we gave it that with Open Source code. This is also why AI is good at generating pictures, stories, or web pages: it is all out there on the Internet. Humans are still better at learning from fewer examples, even though there has been a lot of progress in that area recently with foundational models and few-shot learning.
Now, my belief is that although recent advances are a good predictor for upcoming short-term improvements, it is not for the long term (by that I mean a few years away). I am 100% sure that we will get better copilots and image models in 2024, but I also think it is extremely hard to predict what will happen in 2030. The reason for this is that large foundational models exhibit a property called emergence, which you can basically interpret as: “we know increasing the resources available to the model will make it better, but we do not know exactly at what”. And let us be clear here: that does not mean we do not know how to improve a model along a specific dimension, how to teach it to be better at something specific. This is precisely what I am working on! This means I believe the major advances in AI have not and will not come from such deliberate approaches, but from just throwing more hardware, more / better data, and marginally better core architectures at models, and seeing what they learn to do.
In other words, I do think AI may get better at the things it doesn’t do well enough to replace programmers, but I would say it is about as likely to happen to other “intellectual” professions such as managers, lawyers, doctors, architects, and so on.
One point that I’d like to emphasize though: a few years ago people who don’t work in AI used to think that “creativity” was a human prerogative and that AI would never challenge “artistic” professions. I hope by now you have figured out this was wrong. I’ll go even further: one of the main problems in AI today is that it tends to be too creative. We keep trying to figure out ways to curb that tendency to be like a child who tells you about their day at school but invents half of it. My personal view on it is that this is not something we learned about AI, this is something we confirmed about human nature: that evolution made us powerful pattern-matching machines, and that creativity is just an emergent property that came out of it. And I say confirmed because personally I have always thought it to be the case: there were hints at it even before AI, from observing how children learn to our unconscious biases.
That digression aside, let us now assume AI does effectively replace programmers. What then? Does that depress me? What am I to do with my life?
Well, the truth is I am not concerned. Programming is an essential part of me but I had been writing code for over a decade before I started doing it professionally, and even nowadays I still write some code for fun with no financial reward at all. I’ll keep doing that even if AI beats us at it. Did people stop playing chess or any of the many games AI has beaten us at for years? Did we stop making tools and furniture by hand now that most of the industry is automated? Of course not. So I’ll probably turn to a form of craftsmanship I already enjoy practicing, or even to more artistic forms of code.
Will I still make a living out of it? Probably not. But I am confident I will adapt. Even if the entire tech industry is automated I know that I can learn other unrelated marketable skills in a matter of months if not weeks — because I have already done it. And I am in a better position than most to notice what jobs are under threat.
Extrapolating to the entire society, will it not result in a major problem if too many jobs are automated? Well yes, it will, and I do believe at some point we will need to tackle that issue. Note that the issue is not that there won’t be enough jobs for everyone. Jobs are not something we fundamentally need, if anything most of us work too much today. And it is not that there won’t be enough value creation for everyone either: those machines will create value. The problem is one of allocation.
Here is how I explain my view on it: I actually like capitalism, and I think it is in part responsible for a lot of the progress — including social progress — that has happened in the world in the last few centuries. However, capitalism was designed on the assumption that the majority of the value-add comes from human work, and a minority from capital. Automation will reverse that, and in a system where capital is responsible for a large majority of the value-add capitalism breaks down.
So how do we prepare for this? Well to start let me recommend a short novel I already linked earlier. But I think the most important thing to understand is that we are not there yet, and the worst thing to do would be to try to implement parts of the endgame now. See, this is not a zero-sum game, but the world is not a bunch of altruistic parties either. So if you start — for instance — taxing machines now, or worse trying to slow technical progress, rest assured some of your neighbors won’t, and the end result is they will own the machines and not you. So the only reasonable thing to do if you are a benevolent but realistic government is accelerate as fast as you can, capture a large enough chunk of that capital, and then once things settle down make sure it benefits your population. All that while avoiding exploding under the pressure of the inevitable social unrest in the meantime.
Democratic governments of the world, I wish you good luck and hope you will succeed.
You can only claim to be contrarian if people actually defend the opposite viewpoint. Well, one of the “best practices” I disagree with was recently featured in the Google Testing Blog — usually a very good resource, there is a reason that post appeared in my newsreader!
The authors present two versions of a function and ask which one is the most readable.

At first glance, we can already notice the presentation is a bit partisan :) In addition to the obvious background color bias, the authors ellipsed some code on the right-hand side, making both implementations appear roughly the same size when the right one is really much longer.
The authors argue that the function on the right is more readable because it does not mix levels of abstraction, and that makes it more “top-down”. Well, OK, but the function of the left reads linearly from the top of the screen to the bottom, whereas if you want to understand everything that happens on the right-hand side you need to jump back and forth.
You may tell me, sure, but you never need to look at the whole code, that is what abstraction is for! OK. Quick, tell me this: does the function that bakes the pizza also heat the oven, or do you need to preheat it first? Hint: there are two functions. One is called bake and takes a Pizza, and the other is called bakePizza…
Also, what happens if you pass a pizza to those functions twice? Are they idempotent or do you end up eating cinder?
So, you may have guessed that I do not like the code style on the right. But I have to admit something: it is somehow easier to understand than the code on the left. Is that because of the structure that separates the levels of abstraction? Let us see. What about that version?
func createPizza(order *Order) *Pizza {
// Prepare pizza
pizza := &Pizza{Base: order.Size,
Sauce: order.Sauce,
Cheese: “Mozzarella”}
// Add toppings
if order.kind == “Veg” {
pizza.Toppings = vegToppings
} else if order.kind == “Meat” {
pizza.Toppings = meatToppings
}
oven := oven.New()
if oven.Temp != cookingTemp {
// Heat oven
for (oven.Temp < cookingTemp) {
time.Sleep(checkOvenInterval)
oven.Temp = getOvenTemp(oven)
}
}
if !pizza.Baked {
// Bake pizza
oven.Insert(pizza)
time.Sleep(cookTime)
oven.Remove(pizza)
pizza.Baked = true
}
// Box and slice
box := box.New()
pizza.Boxed = box.PutIn(pizza)
pizza.Sliced = box.SlicePizza(order.Size)
pizza.Ready = box.Close()
return pizza
}
Do you recognize it? This is just the left-hand side function code, with function names from the right-hand side added as comments.
I don’t know about you, but this is my favorite. And it looks like the readability just came from explaining what we did properly here, not from extra abstraction layers and indirection.
In conclusion, I stand by what I said earlier: do not extract small functions from linear code, especially if you only use them once. None of the benefits offsets the loss in linearity.
I wasn’t sure if I wanted to mention this or not, but I ended up editing the post because there is something that bothers me with this function, and it is that business with the oven.
First, pre-heating the oven is self-contained and should probably be a method of the oven itself. But beyond that, this code makes no sense: why would you create a whole new oven to make a pizza? In real life, you get an oven once, and then you bake a whole lot of pizzas with it, without going through the whole heating cycle.
I know this is a synthetic example but this kind of issue actually occurs in real code and sometimes causes performance issues. It is likely that this code should take the oven as a parameter. Providing it is the job of the caller.
(And since you put the pizza in the box, you probably want an interface where you return the box, not the pizza. Oh well.)
“Thoughts” are posts about what has been on my mind. Sometimes practical, sometimes not; often just things I read recently. Less thought out than regular posts.
First a small announcement: I have joined Bluesky. I like it so far and I think it has a good founding team, so I want to believe in its long-term potential. On the other hand, I will likely stop using Mastodon soon; it is not working for me. I will stay on X for now, but I can see Bluesky replacing it entirely after a while.
Let’s acknowledge the difficulties of our times but without accepting defeat in the process of trying to solve those issues. […] We are at the point where we can decide if we want to go full Cyperpunk or something more positive. Solarpunk is the movement, the idea behind envisioning and then creating a better future based on humans which live in harmony with nature.
If there is one thing I am certain of regarding climate change, it is that no amount of moderation will be enough to solve it without terrible consequences on a planetary scale. But there most likely won’t be a magical technological breakthrough either. We need to seriously start looking at technologies and ways of life that are both sustainable and acceptable for the whole planet.
On a side note, I also think important technological breakthroughs are not easily predictable in detail. For instance, I think it is highly likely that AI will play a role in fighting climate change, but I currently have no idea how.
Generative AI introduces much more stochasticity into programming. […] Ironically, this makes the deterministic parts that much more important.
One of the first things I did at Finegrain was making sure I knew about all the sources of randomness, and then adding deterministic end-to-end tests.
The authors have taken the visual understanding part of BLIP-2 (a ViT and a Querying Transformer) and aligned it with Vicuna. They achieved that by connecting them through a single 5120x768 linear layer. This illustrates well the unintuitive effectiveness of simple linear projections in high-dimensional spaces.
It is hard to grasp for some engineers early in their careers what took them here won’t take them there. Shipping features will probably take you to senior, shipping 10x more features is not how you’ll become a principal. What will get you to principal is removing chaos, unblocking others, fighting for things to be built in the right order, fighting scope creep, having backbone while knowing when to delegate decision making.
To fully understand a “best practice” or why something is necessary, it’s important to experience how things go wrong without it. When teaching programming, we should let people make these mistakes, and [only] then show them the tools to correct them.
There is a whole book dedicated to the fact that happiness fuels success and (mostly) not the other way around, and how to take advantage of it.
I haven’t read the book (yet) but I have come to that conclusion independently for a while now. I found the book through this post which is one of several about people moving back from management to engineering.
The Three Little Pigs is a popular bedtime story, but it strikes me as unrealistic and not teaching the right lesson. So I decided to revisit it.

Image by Freepik
The Three Little Pigs — A Cautionary Tale about Over-Engineering
Once upon a time, there were three Little Pigs who lived in the forest. One day, they learned that a Big Bad Wolf would soon come to the forest. They decided to build houses to ensure their safety. The first Little Pig decided to build a straw house, the second one a wooden house. As for the third one he elected to build his home out of stone.
A few days later, the Big Bad Wolf arrived in the forest. He saw the straw house of the first Little Pig. He knocked on the door, asking to come in. The Little Pig refused, so the Big Bad Wolf huffed and puffed and blew the house down. Scared, the Little Pig ran off. Thankfully, the Big Bad Wolf was too exhausted from blowing so hard to catch him. The Little Pig sought refuge in the wooden house of the second Little Pig.
Once rested, the Big Bad Wolf followed the tracks of the Little Pig, and soon he arrived at the door of the wooden house. He knocked on the door, asking to come in, but the little pigs refused. So the Big Bad Wolf huffed and puffed and… nothing happened, because how the hell would a wolf blow down a wooden house?
Disappointed, the Big Bad Wolf gave up and walked down the road, giving up on a pork meal for the day. But as he was leaving, he saw the stone house of the third Little Pig. And he did not knock on the door. Because there was no door, no roof and no windows. The third Little Pig simply did not have the time to finish his house, and he was still there with his trowel, spreading mortar.
For the sake of the faint-hearted, this is where the story concludes. Let me just say it does not end well for the third Little Pig.
Here it is, a much better twist on the classic.
Ah, and of course the moral:
Trade-offs are everything, pick the right ones!
When I interview software developers, I usually ask some variation of the following questions:
What Software Engineering practices do you consider critical to achieve your objectives? Are there “best practices” that do not matter, or which can be actively harmful?
My goal in asking this is mainly to understand if the candidate is opinionated, and if so where those opinions come from. For the most part there is no right or wrong answer, but there are good and bad ways to answer.
I have already posted some of my answers to the first question here. Some can be reversed to become answers to the second one too:
Don’t Repeat Yourself is easily one of the most abused principles. It leads to unnecessary over-abstraction and coupling. See “AHA” in the Alternatives section of the Wikipedia page.
“Don’t ship the Org Chart.” Conway’s Law is not something you can easily fight, so odds are you will ship the org chart. Instead, acknowledge Conway’s Law and architecture both your software and organization with that in mind.
In addition, here are some of my other answers to that second question:
Putting a limit on function sizes, and systematic method / function extraction. This appears to make code superficially more readable, but in reality it makes it less linear and harder to debug. Other posts sharing that opinion: Martin Sústrik, Loup Vaillant.
Systematic Test-driven development. TDD is very useful when the problem is well-defined and the implementation error-prone, for instance when implementing precise specs or an RFC. It is harmful when you do exploratory programming. In some code bases, I go as far as having no unit tests — only end-to-end and regression ones, in addition to plenty of inline assertions in the code.
Trying to achieve strict typing of dynamic languages. Don’t get me wrong, type-checking is helpful as a tool to catch bugs and guide development. But trying to adhere to some theoretical principles too strictly can make you lose all the benefits of the dynamic language underneath. If you want to make that trade-off, great, but then use a statically typed language instead! There are good reasons why TypeScript is unsound.
Writing extensive documentation. Some people tend to think documentation is a silver bullet, and it is often because they have a bad mental model of how users behave. There are two main ways users — including other developers — will possibly consume your documentation: they will read the Getting Started / tutorials, and then they will look for solutions when they encounter problems. Optimize your docs for that.
“Elegance” is overrated when writing software for production. It should usually be traded off for robustness first — i.e. lower MTBF and MTTR, which implies understandability — and performance second. “Elegant” is great if it means “straightforward”, harmful if it means magic.
To end this post with something related, Brett Slatkin is hosting a podcast called Worst Practices where developers talk about something terrible they do. I am sure you will recognize yourself in some of those. For instance I have only used a simple editor for a long time like Mitchell Hashimoto does, I debug with print statements like Leah Culver and I even sometimes put them in system libraries like Mahmoud Hashemi!
Ten years ago, I started a French-speaking mailing list about Lua. It was somehow active for around two years, but there was never really enough of an active community for it to keep going. To accompany that mailing list, I bought the domain luafr.org and set up a small static webpage with a few links about Lua in French.
Recently, my registrar Gandi was acquired by Total Web Solutions. Prices went up, service went down, so I will be migrating my domains elsewhere when they renew. I am also using the opportunity to let go of a few unnecessary domains, and luafr.org will be one of them. It will expire on October 9, 2023.
Nobody should miss the static webpage, but there is one important thing hosted on that domain, and that is the only LuaRocks mirror enabled by default besides the official website and GitHub.
I have opened a pull request to change the URL to loadk.com, which points to the same IP, but if you have somehow hardcoded it in your configuration file please change luafr.org to loadk.com to avoid disruption.
I have done that three times now, so it is time to make a quick blog post about it.
When you create a new Google Cloud Platform account, its VPC firewall comes configured like this:

The first two rules, default-allow-http and default-allow-https, only apply to machines with the http-server and https-server network tags and they are fine. The third one, default-allow-icmp, allows ICMP which is the protocol used by ping; some people do not want that but I typically keep it on. The three last rules, though, are too lax for my taste.
We start with default-allow-rdp. The Remote Desktop Protocol is typically used to access remote Windows servers, even though it is now also supported by Gnome. You are probably not a Windows shop and you certainly do not want that open on all your instances, so disable it.
Now, default-allow-ssh. It is the same thing for the Secure Shell Protocol. I typically do not want to allow SSH access to instances from the outside; instead, I enforce the use of Identity-Aware Proxy through gcloud like this:
gcloud compute ssh --tunnel-through-iap my-instance-name
So you can disable that rule, but you also need to create one which is exactly the same with the source set to the IAP IP range i.e. 35.235.240.0/20. I call it iap-allow-ssh.
You may also still want to allow SSH access from anywhere on some machines such as SFTP servers, so I create a rule with source 0.0.0.0/0 that only applies to machines with the sshable network tag.
I also enable logs on both SSH rules so I can easily know exactly who connects to instances and when.
Finally, the default-allow-internal rule allows all traffic between machines in your VPC. For some of you it may be fine, but I prefer configuring all flows explicitly using tags, so I disable it as well.
This is how it all should look eventually:

“Thoughts” are posts about what has been on my mind. Sometimes practical, sometimes not; often just things I read recently. Less thought out than regular posts.
Mitchell Hashimoto compares the growth of AI today to what happened with Cloud Computing and finds many similarities. He also points how that was not the case for Web3 (cryptocurrencies).
I love creative job titles and Jaana Dogan found a great one. When you have the experience, you immediately understand what it means.
At Lima my “unofficial” job title was VP of Data Corruption, and Denis was Chief Packet Loss Officer. I never went as far as putting it on my LinkedIn though ;)
Scott Chacon tweeted about how he thinks that personalized AI-based tools will change education and his concern that some countries will not adapt fast enough. I share both parts of this opinion.
I have had my own issues with how education works in France and I have been thinking about this for a long time. Neal Stephenson’s book The Diamond Age is a must-read about that topic, and also yet another warning to make sure AI does not end up ossifying the privileges of an elite class.
I want the Primer, but IMO the current breed of foundational models is poorly adapted to the task. We need online training and scalable user personalization. I am hopeful that will get there though, because there are other lucrative use cases with similar requirements.
Over 10 years ago, I created this blog and wrote the static generator that powers it. I used Markdown for the articles and lunamark, a library written by John MacFarlane of CommonMark and Pandoc fame.
Recently, John created djot, a markup syntax intended to be an evolution of Markdown. The initial reference implementation was written in Lua and I decided to port my static generator to it.
Converting the Markdown parts of the post themselves was not very hard, the main think I can think of was switching my old posts from intented code blocks - which are not supported by djot - to fenced code blocks. However, my posts were not pure vanilla Markdown. I used two specific features from lunamark and a few extensions of my own.
The first lunamark feature I had to find a replacement for was Lua metadata. djot does not support metadata yet but it does support raw blocks with a format which I could easily abuse.
The second feature is Pandoc-style title blocks, which I could just move to the metadata section so I did that.
I also had a custom block format syntax that I used for three things: raw blocks, syntax highlighting language choice and post descriptions. The first two are natively supported by djot, and I moved the description to the metadata as well.
For those transformations I used Perl one-liners such as this one which converts the metadata format:
for i in separateconcerns/blog/articles/*.md; do
perl -0pe 's/<!\-\-@(.*)\-\->/"```=lua-meta". join("\n", split(\/\n \/, $1)) ."```"/es' \
$i > "$(dirname $i)/$(basename $i .md).dj"
done
Once that was done, it was time to port the generator itself. I started with the HTML part. As I said some of the things I was doing manually in lunamark are supported natively in djot, there were two things left to do: support my metadata blocks and detect posts with code in them so I can include the JavaScript syntax highlighting library only when needed.
Djot has a step when you can get the AST and I could have used it to extract this information, but I decided to use the same method I used with lunamark instead, which is overriding parts of the renderer. To do that I created a small helper library which mostly exposes the StringHandle used internally in djot.lua. I would need it for the next part anyway.
All in all, it was pretty smooth. I just hit a small issue with URL parsing which I worked around for now by using URL encoding in my .dj source files.
Since I did not change my output format much and kept exactly the same metadata, my index and Atom feed generation worked out of the box. But there was still one last thing to do: because this blog is also available on Gemini, I had to implement a Gemtext renderer.
In lunamark, there was a generic writer which I overloaded for that purpose. As djot.lua only supports HTML output natively for now, this time I just wrote a renderer from scratch. I could also have gone the Pandoc route as djot includes a custom writer and reader, but I wanted to avoid it and Pandoc does not natively support Gemtext anyway.
All in all, this took a few hours to do, and the blog post you are reading now has been published with this new stack. I like djot, it is an improvement over CommonMark even though it is still has a few rough edges. I like parts of the djot.lua library a bit less, especially the parser. lunamark uses LPeg instead and I find it simpler, but it’s still fine.
This experience also conforts me in the choice I made 10 years ago to write my own static generator using a language I know well with few dependencies. It is very low maintenance most of the time compared to the likes of Hugo, Pelican or Jekyll, and it lets me do changes like this easily when I want to.
It’s been a week since I left Inch, and now it is time to disclose what I am up to.
Last Monday I joined a very early-stage startup called Finegrain. We’re working on improving online images using AI with no humans in the loop. The founders of the company are the Moodstocks founders Denis and Cédric, so we get to work together again after 10 years.
I won’t hide it, I am not an AI specialist. Even though I keep informed on advances in the field, I have read books and written some deep learning code, I am far from the level required at a company like that.
I do intend to learn more, but it turns out we may also need the things I do have experience with, such as building scalable and resilient systems with few resources, designing simple APIs to do complex things, integrating with customers and more generally being a technical jack-of-all-trades at a small company :)
If you know about AI though, we are hiring!
In two weeks, I will no longer be an Inch employee. I joined the company a bit over three years ago, but it feels like much longer. Back then I had no child, and the first thing the word “Pandemic” brought to mind was a good board game.
Three years later, Inch is not so different from the company I joined, and yet much better in many ways. As for me, I did what I came for initially, and then more. Now I have an opportunity I could not pass. It is too early to talk about it except in cryptic form, but when I do you will understand.
So I close this chapter of my professional life, leaving Inch and its team. Which is the hardest part, because they’re a fun and endearing bunch, and we’ve been through those not-so-great years together, and we’ve made so many memories. And now we’re friends. But well, I am not leaving Paris, so it’s not like we won’t see each other again. :)
And by the way, Inch is hiring a software engineer! It is a French-speaking, semi-remote position. Get in touch if you are interested.
“Thoughts” are posts about what has been on my mind. Sometimes practical, sometimes not; often just things I read recently. Less thought out than regular posts.
When I was a student at IMT Atlantique (back when it was still called Telecom Bretagne) I took an optional class about the history of telecommunication systems. It was mostly taught by retired engineers from the field.
It was a fascinating class. A recurring theme was what an engineer’s job was about. One of the teachers, who had been instrumental in the development of electronic switching in Europe, said something among those lines: “a good engineer delivers systems that are scalable, cheaper, and ship faster”.
I have been thinking about that topic lately, and even though I will not deny the “it’s all about trade-offs” line I have been using for years, it is good to remember that those trade-offs should be made only on the optimum curve.
Add to that the idea that engineers should know what systems not to build, and you have my definition of what makes a good engineer.
Karl wrote something interesting about using the word We to talk about a company or a team. He wrote it in French so here is a rough translation:
I often see a company’s employees use the word We (“nous” in French). For a very long time I have been trying to avoid using this turn of phrase in my mail, bugs, etc. For instance, at Mozilla, I used to say “the Webcompat team” or “Mozilla”, or to change the sentence to focus on the product itself.
Instead of “We should add feature H to solve this bug”, I would say “Product A needs feature H to solve this bug”.
When I notice myself unconsciously using “we”, I correct it immediately. But why? To preserve the emotional distance between work and pseudo-belonging to a company.
“We” has a very inclusive meaning of identity for me, and so I avoid it in contractual relationships. The company no longer has a “we” when the time to reduce its workforce for financial gain comes. It is one of my struggle techniques, a small resistance.
I understand what he is saying and it makes a lot of sense, yet I make a different choice. In fact, I cannot fathom working for a company or at least a team where I would not feel comfortable saying “we”.
It’s the same uneasy feeling I have when I see people say that small startups branding themselves as family is a red flag. I understand why they would say that. And sure, we are not family, but when you work that closely with people for years, I think there should be some sense of belonging. Maybe “friends” is a better word.
Also, I am never going to work for a company if I am not aligned with its mission. If I start trying to distance myself from it and notice it, things will have to change one way or another.
This tweet by Jaana Dogan made me remember this blog post by Erik Bernhardsson, which I often find to be true, especially when dealing with startups.
Never attribute to stupidity that which is adequately explained by opportunity cost.
A great post by Andrew Bosworth (CTO of Meta) about how some people such as John Carmack can have an influence even after they’re gone.
A long post by Evan Martin about his experience working on Chrome from 2007 to 2012. Toward the end, he turns to the state of Chrome and the Web today.
I love this kind of retrospective post, which are full of anecdotes, deeper insight, and food for thought.
By the way, Evan posts interesting things on a variety of topics. I have been subscribing to his feed for a very long time, I think for Arch-related content initially. You may want to look at his other posts as well.
Here is DHH saying something I’ve been saying for a very long time as well, but it bears repeating. There is way too much stuff happening to follow it all. If you want to last in this job, especially as a generalist, you must figure out the fundamental things that will matter even more in 10 years than today, and ignore most of the rest unless it’s a deliberate strategy.
Even though it’s not my main focus anymore, I am still following CRDT-based state synchronization systems attentively. Two projects have caught my attention recently. The first one is Braid which has been progressing steadily, and the second is Automerge which has announced a major 2.0 release.
When I was hired at Inch my “official” position quickly became an issue. For various reasons neither the title they wanted to give me initially (Engineering Manager) nor the one I would be given externally (CTO) nor the obvious Senior Software Engineer worked. So I proposed to help them define the next position on the technical ladder.
What follows is a translation of a document I wrote in French, slightly edited to remove Inch-specific details. It is still written from a European - and more specifically French - point of view. That document was later used to promote a 2nd engineer to Staff. I publish it here so it can help people needing to do something similar elsewhere.
Typical early career levels for software engineers (or developers) are relatively well-known:
Progression is based on experience and it is expected that a good engineer will reach mid-level after 2 to 5 years of experience and senior between 5 to 10.
Senior developers have two ways to evolve: branch into management or stay on the technical track. This document describes the second option.
Let’s start with a few key things to understand regarding management. A development “team” (project or run) is typically 3 to 10 people. It has a “lead”, which is not a level or a management position. It is a technico-organizational role assumed by a Senior+ developer at a point in time.
The names of levels after Senior differ depending on companies but can be for instance:
At some large tech companies the “Distinguished” level is equivalent to VP and Fellow to SVP or CXO. At Inch, Staff corresponds to level F. The difference between Staff+ levels is often defined by impact:
Those levels constitute a pyramid where each stage must be much smaller than the one below (e.g. by a factor of 5 at least) otherwise they stop making sense.
Given the size of Inch, only the Staff level makes sense. A Principal level may not be created until the company is large enough to have several independent technical teams (at least 12 developers, ideally around 30). Consequently, some things relating to strategy and aura are expected from the Staff level at Inch.
A key point is that Senior is a “terminal level”, which means it is not expected that all Senior engineers will become Staff at some point. An engineer may very well remain at the Senior level for their whole career.
Progression from Junior to Mid to Senior is very related to experience and hard skills (soft as well, especially for Senior). But the value of marginal experience goes down with the years and is harder and harder to measure.
The Staff level is still partially related to experience - at least 8 years are expected in general - but more importantly to impact within the company. This means hiring at this level is rare, and it is not unusual for a Staff Engineer to go back to Senior for a few years after a job change.
Important note: Staff is not a management level but it does require more soft skills than previous levels (more on that later).
Staff is a technical specialization level, hence not all Staff engineers look the same. To understand the differences, here are a few axes.
A developer can specialize in one or several fields or technologies and become a true technical expert. For instance, they can be a master of their language and framework and contribute to their core or ecosystem. They can also become experts in a business domain. Those are depth-first approaches.
Others, on the other hand, expand their knowledge horizontally and know about numerous domains, technologies and parts of the stack. This is the generalist, breadth-first approach.
In general it is rather a mix of both approaches: developers with a very wide background and a few specialty topics they master. We talk about T-Shaped people.
Will Larson defines 4 Staff+ archetypes: tech leads, architects, solvers and right hands.
A Staff Engineer should be able to:
Grapple with a complex, poorly documented system quickly. For instance, identify the root cause of a bug in a code base they do not know well.
Understand what limits a system, anticipate problems and propose realistic and pragmatic architectural changes while taking existing assets and resources into account.
Know and understand high-level software and system architecture principles (Conway’s Law, Gall’s Law, permission models, different kinds of data flows…)
Communicate with their team as a Lead, organize a project, implement new processes.
Communicate with their management: report their results and those of their team, give visibility on load and work in progress.
Communicate outside the company, for instance: with customers, with prospects, with candidates, with investors, with Open Source communities…
Deal with a crisis, for instance organize production incident response.
Analyze a development opportunity while taking all company-level parameters into account (cash flow, strategy and positioning, long-term vision). Think ahead 2 / 6 / 18 months.
Understand the concepts of backlog, velocity / capacity / pressure. Know how to prioritize, understand when to say yes and when to say no. Know how and when they should defend their opinion depending on the topic (when to “be right”).
Think ahead developments and choices, considering impact on maintainability, stability, evolutivity, complexity and overall performance.
Anticipate hiring needs and departure risks. Understand hiring may take 6 months, plus 6 extra months to become really efficient. Understand that at Inch scale not everyone is replaceable, but still reduce risk for the company in the event of a departure, including their own.
Do technical watch, implement new tools and processes when it is necessary - and not when it is not. Retiring tools and processes whose benefits are dubious. Tech watch channels available include HN / Lobste.rs / Tilde, blogs, podcasts, meetups, the GitHub feed, social networks…
Be visible. Communicate internally on their progress and that of their team, in spoken and written form.
Develop their own network. At this stage of their career, an engineer must know people to get in touch with regarding specific topics, recruiting, etc. This kind of network is developed through previous experiences, participation in meetups and conferences, Open Source contribution and so on.
A Staff Engineer should tick several (not all) of those boxes:
Be the designated expert of one or several significant parts of the technical stack or technical topics (e.g frontend, backend, infra / deployment, security…)
Be the designated expert of one or several significant parts of the product’s code base.
Have realized or led a development or technical evolution which is undoubtedly an important achievement at company level, recognized outside the bonds of the technical team. This kind of very visible project can be handed to a Senior engineer ready to evolve to Staff as a Staff Project.
Mentor one or several junior developers. This is in “achievements” and not “competences”: any Staff - and even Senior - developer should be able to help a junior get better or understand something, but some can be true career accelerators for the juniors they work with.
“Thoughts” are posts about what has been on my mind. Sometimes practical, sometimes not; often just things I read recently. Less thought out than regular posts.
I have had several discussions about how SSO should be priced by SaaS providers. On the one hand, making it prohibitively expensive weakens security for everyone, and there is a website about that. On the other hand, features that distinguish between business categories are often related to security and compliance (audit logs, SLAs, etc).
My personal position - and I do not speak for anyone here including my employer - is the following:
I often say SOA started to become mainstream when Jeff Bezos mandated it at Amazon around 2002, a story famously told by Steve Yegge. But Werner Vogels has shared a document that shows the move began in 1998 and was advocated by engineers.
The Manifesto already emphasized moving data to the process, hiding data models from clients, data consistency issues… A fascinated read, almost 25 years later.
This essay by Aaron Swartz is ten years old. A good time to read it again.
An organization is not just a pile of people, it’s also a set of structures. It’s almost like a machine made of men and women. […] True, sometimes you have the wrong gears and need to replace them, but more often you’re just using them in the wrong way. When there’s a problem, you shouldn’t get angry with the gears — you should fix the machine. […] You can’t force other people to change. You can, however, change just about everything else. And usually, that’s enough.
Greg Wilson would like a journal of peer-reviewed summaries of research findings. No new results, just good explanations. Me too!
“Thoughts” are posts that relate to things that have been on my mind recently. Some are practical and some are just reflections on a given topic. They are less thought out than regular posts.
So, the Bird, of course. Not sure if or when it will collapse. Better safe than sorry so I am planning my retreat to the Pachyderm, but I do not really believe in the Fediverse model.
Eric said on Twitter - where he deletes his tweets so I won’t link it - that it might be time to bring back RSS/Atom feeds. I agree, especially since I finally found a feed reader I like. I know I won’t have the discipline to post as frequently as him, Karl or Rob, still that’s what those “Thoughts” posts were supposed to be for initially.
But feeds by themselves are not enough. The value is in the graph and the discovery that comes with it. I follow about 450 feeds, and you cannot see which ones. I won’t publish my raw OPML though. It’s not properly categorized so it wouldn’t be very useful anyway. It’s like my booklist, which I consider deprecating soon: too many items, not enough value.
Maybe the solution is to publish lists of links here from time to time like Daniel. You can also follow my Pinboard, which has feeds. Lobsters is great too.
Something else feeds don’t do is discussion / debate. But maybe that’s a good thing; I was growing tired of that aspect of Twitter lately. I think I prefer topic-specific spaces to social networks. I’m on a bunch of Slacks, Google Groups and mailing lists already. I liked forums but they’re out of fashion.
Oh, and for private messages, well… just send me an email, I guess.
Today I attended Lua Workshop, and Roberto Ierusalimschy’s keynote was about Pallene, a language designed as a system counterpart to Lua in a scripting architecture. The language is a typed subset of Lua and can be used to replace C or as an interface between Lua and C. It is still a work in progress, there is no stable release yet.
Pallene works by accessing the internals of the Lua VM, so it requires a patched version of Lua. I wanted to try it so I added support for it to localua. You can just use “pallene” instead of the Lua version and it will pull the latest sources of Pallene and its Lua fork and install them in a self-contained directory.
The Pallene compiler does not have an easy way to specify a local Lua directory and I decided against hacking it so you will have to pass CFLAGS as an environment variable for it to work.
I have just pushed a localua version that supports this so you too can try Pallene now. Be aware that I have only tested this on Linux, and that it is an undocumented feature which means I do not guarantee I will support it forever. That being said, this works now:
curl "https://loadk.com/localua.sh" -O
chmod +x localua.sh
./localua.sh .lua pallene
curl -O "https://raw.githubusercontent.com/pallene-lang/pallene/master/examples/fibonacci/fibonacci.pln"
CFLAGS="-I $(pwd)/.lua/include -O2" ./.lua/bin/pallenec fibonacci.pln
./.lua/bin/lua -e 'print((require "fibonacci").fibonacci(10)[8])' # prints 13
In the list of application architecture patterns I like to use, Dependency Injection comes rather high. Here is how and why I use it.
If you have already seen dependency injection used in practice, you may believe it requires the use of a framework such as Dagger. Although I did use such frameworks - especially in Angular where it is native - most of the time I use a simpler form of DI where I just pass dependencies to functions or constructors.
For instance, in Lua object-oriented code, instead of writing this:
local json = require "dkjson"
local obj_mt = {
__index = {
as_json = function(self)
return json.encode(self.data)
end
}
}
local function new_obj(data)
return setmetatable({data = data}, obj_mt)
end
local o = new_obj({foo = 5})
o:as_json()
I could write something like this:
local obj_mt = {
__index = {
as_json = function(self)
return self.json_encoder(self.data)
end
}
}
local function new_obj(data, json_encoder)
return setmetatable(
{data = data, json_encoder = json_encoder},
obj_mt
)
end
local json = require "dkjson"
local o = new_obj({foo = 5}, json.encode)
o:as_json()
Note that this follows the Dependency Inversion principle, which is often confused with dependency injection but not exactly the same thing. It may not be obvious in a dynamic language such as Lua but the implementation of obj implicitly defines the signature of the JSON encoder it expects, and you may substitute the encoder for something else.
Also, on a stylistic note, in a larger program I would probably not pass every injected dependency as a different parameter, instead I would use a single container for this.
In case you’re interested, here is the same example closer to how I’d write it in practice in Teal:
local type JsonAble = {string: any}
local type JsonEncoder = function(JsonAble): string
local record ObjDeps
json_encoder: JsonEncoder
end
local record Obj
data: JsonAble
as_json: function(Obj): string
_deps: ObjDeps
end
local obj_mt = {
__index = {
as_json = function(self: Obj): string
return self._deps.json_encoder(self.data)
end
}
}
local function new_obj(data: JsonAble, deps: ObjDeps): Obj
return setmetatable({data = data, _deps = deps}, obj_mt)
end
local json = require "dkjson"
local o = new_obj(
{foo = 5},
{json_encoder = json.encode as JsonEncoder}
)
o:as_json()
Now let us see a few use cases for this.
Probably the most well-known use case for DI is simplifying testing of code that does I/O, for instance code that makes network requests or interacts with a database. It makes it easy to replace the problematic part with a mock. You can mock just what you need or the entire dependency, for instance you can replace Redis with fakeredis.
This is a less well-known but very good use-case, especially in Lua. Let us consider my JSON example above. Users of this code may want to use dkjson, but maybe they want to use a faster, pure C module, or maybe they are running in OpenResty and have cjson available. With dependency injection, it’s easy to just use it.
This is in fact the use case that prompted me to write this blog post. I was writing some algorithmic code in Teal for which I had a reference in Python, and I wanted to apply the dual-implementation comparison technique I like. However, this was AI code that had random parts.
What I did was that I refactored the code in both languages so that all sources of randomness were injected, and then I injected deterministic pseudo-random values instead, which meant I could now compare the state of both implementations at all times and easily debug things such as numerical problems.
DI is a great tool, but it tends to make code a bit more complicated, so for instance I do not use it to inject pure functions or libraries from the same codebase.
If you have used Angular with Storybook, you may know what abuse of DI looks like. It can be super verbose because sometimes you need to inject a lot of things you do not really care about for it to work.
A way to work around that if you still want to use DI may be to have sensible defaults for some injected objects. That way you can still override them if needed, but you can still have simple sample code.
Here I want to discuss a system design pattern that I call “push-to-poll”; if it has another name I am not aware of it. I have applied that pattern successfully a few times, and used systems that did not but should have way too often.
Suppose you are responsible for a “server” system. That system generates events (messages), and authenticated “clients” need to receive those messages.
A simple solution is to provide some kind of feed: the client authenticates and provides a small piece of state (a timestamp or a cursor) which allows the server to return the messages they haven’t processed yet. Ideally individual messages also have properties which allow the client to process them in an idempotent way, e.g. skip messages it has already processed.
EDIT (2022-04-04): I was made aware through HN of a spec called HTTP Feeds which looks like a very good format for those feeds. Something like Atom can still work too.
The client can then just process the feed in a loop, in pseudocode:
cursor = INCEPTION
loop do
messages, new_cursor = fetch_messages_since(cursor)
for message in messages do
if not processed(message)
process(message)
end
end
cursor = new_cursor
sleep(DELAY)
end
This solution is called polling, but the main problem with it is the DELAY variable. If it is set too low, both the server and the client will keep doing work and exchanging data for nothing, but if it is too high messages won’t be received by the client in a timely fashion.
If we want message reception to be realtime, we may use a push-based design instead. In that model, the client starts by registering with the server. The server will then send the messages to the client as they arrive.
In practice you might not really want to send each message on its own so you will send message batches instead and client code looks like that:
def messages_handler(messages)
for message in messages do
process(message)
end
end
register_with_server(messages_handler)
This design, however, has a few issues as well.
First, there is the bootstrap problem. The client might need messages that arrived before they registered. The server could theoretically just send all the previous messages to every client on connection, but in practice this is almost never what you want because of volume, performance, etc.
Even if you don’t need to bootstrap, what should the server do when it tries to send a message to the client and it doesn’t work? It could be that the client has gone down, or it could just be a network issue. In some systems the server keeps the failed messages for a while and retries, but that complexifies the design because it means the server needs to keep per-client state.
Finally, in practice, authentication is often more complicated that way. The server needs to authenticate the client, of course, but the client also needs a way to make sure the data it receives comes from the server. Here we might need to take a few real-life examples: the client could be a single-page app, in which case the push channel would be something like SSE or websockets; it could also be two HTTP servers communicating in which case we are talking webhooks and might need some ad hoc signature scheme. And by the way, the GitHub scheme does not have any form of protection against replay attacks, which might or might not be a problem for your use case…
The push-to-poll pattern combines the best of both worlds. The idea is simple: start with the same feed as polling, but instead of using a delayed loop to decide when to fetch it let the server tell you like with the push pattern.
It looks like this:
cursor = INCEPTION
def poll_now()
messages, new_cursor = fetch_messages_since(cursor)
for message in messages do
if not processed(message)
process(message)
end
end
cursor = new_cursor
end
poll_now()
register_with_server(poll_now)
It solves the problems from the poll design, because there is no more arbitrary delay. The server tells clients when to poll, which might be every time a new message comes, or with a slight delay to optimize the number of requests and ensure messages are processed in batch. Clients might decide not to poll immediately when they receive a server push as well.
There is no more bootstrap problem or reliability problem since it is built on the poll design: every time you poll, you catch everything up. If you want to be extra safe and messages are very rare, you can decide to poll even if you didn’t receive any message after a reasonably long delay.
The authentication story is better than in the push case, because pushes contain no data. All data access uses regular, client-calls-server authentication. It is still a good thing to authenticate server pushes (for various reasons I won’t go into) but mistakes there have less impact, especially if you have client-side rate-limiting.
There are many small tweaks that can be added on top of that design, for instance you can include a little bit of metadata in the push messages to let clients decide whether they should actually poll or not.
Finally, note that you get polling for free! Implementers who do not need realtime can just use the feed endpoint with the polling pattern.
The only drawback is that it takes an extra request for every exchange, but in practice I have seen very few cases where the server keeps message history where it would not have been an improvement over server push. So I hope you think about it next time you consider serializing data into websockets or webhooks.
I have wanted to play with Gemini for while now, so today being the first day of FOSDEM I decided to do what I would have done if I was in Brussels and hack on something between two talks.
I ended up adding Gemini support to the static generator I use for this blog, which meant implementing a Gemini writer for Lunamark, plus a small trick to display links in the right place.
To host it, I went with Agate which is a simple static content server; I will revisit this choice if I ever need dynamic content.
If you want to check it out, point a Gemini browser - I use Lagrange - to separateconcerns.com.
Over the years, I have accumulated links to blog posts I intend to re-read every time find myself in a position to influence the early stage of a company.
I thought it might be helpful to someone if I published part of that list here. I will avoid links which are too technical and focus on links that can be interest non-technical founders as well.
Note that I haven’t included books. There are many good books for founders, but if you must read only one make it The Four Steps to the Epiphany.
If this isn’t enough for you, you’re always welcome to explore my Pinboard account.
Let’s get started by mentioning posts from this blog which transcribe the opinions of two influential people, which I share (on those topics).
Werner Vogels on Skills, where the CTO of Amazon.com describes the qualities he is looking for in leaders at Amazon, in particular Ownership and the willingness to get their hands dirty doing operations.
Hacker Founders, where Paul Graham, founder of Y Combinator, explains how programmers have an edge as company founders, including as CEOs.
Michael Lopp (Rands) on issues with giving people titles. Personally I have never liked titles, which can be a source of conflicts and bad decisions. I have also often held titles that didn’t really correspond to the job I did - which was almost always multi-faceted. Yet, titles are a proxy used by people who don’t know you to understand what you do at work.
Like Rands I don’t have a good solution to that. I write some blog posts explaining what I do in more details that a title ever could, for people who care.
In any case I always suggest avoiding giving some titles to people too early, including to founders: having a “technical co-founder” instead of a CTO will make things easier if you grow fast and need to hire someone later on.
A list of questions to ask yourself when founding a software company, by Michael Marsiglia, CEO of Atomic Object. It can also be a good idea to ask them before joining one.
A simple but powerful rule of thumb for SaaS companies by Brad Feld, relayed by Fred Wilson:
annual revenue growth rate + operating margin should equal 40%
Jason Cohen explains how a startup can disrupt a large competitor by making a part of their business unprofitable and forcing them to abandon it. It’s not as simple as this summary makes it sound, and it’s not the Innovator’s Dilemma. Read the article :)
Kyle Neath on how, in the early days of GitHub, everyone was a “product designer”. Many successful SaaS products started with the same concept:
We only hire smart people we trust to make our product better. We don’t have managers dictating what to work on. We don’t require executive signoff to ship features. Executives, system administrators, developers, and designers concieve, ship, and remove features alike.
Actually, I think I have never seen a company ship good product fast without doing some version of this.
If you don’t know that series of blog posts, you are missing something. They are super insightful essays by a multi-successful founder about building companies.
Ben and Jerry’s vs. Amazon - How to decide between slow profitable growth or fast VC-funded growth.
Chicken and Egg Problems - Understanding the Chicken and Egg problem and some ways to solve it.
Let Me Go Back! - On barriers to entry, and how letting customers understand they can easily move out of your product (maybe back to what they used before) is important.
Bloatware and the 80/20 Myth - 80% of software users use 20% of features, so you can win the market with just 20% of the features, right? No, because not everyone uses the same 20%! I can’t state how often I meet founders who don’t understand this.
Strategy Letter V - How companies have an advantage in commoditizing their complements, and how this can explain some investments in Open Source software.
(There is at least a sixth strategy letter, but it is less interesting IMO.)
This post used to be on Ev William’s blog which went offline some time ago, thankfully I mirror articles that interest me that much.
Just a tweet, but an important one, by William Pietri:
Your company’s most valuable resource is people giving a shit. Ask yourself: does your system encourage or discourage that?
A recent post by Avery Pennarun, Tailscale CEO. It shows how the best strategy to acquire customers is often to solve a single Want - something their current solution doesn’t provide - and then to resolve all their Needs (blockers) so they can actually flock to your product instead of attempting to implement any more Wants.
A slide deck by Rodrigo Sepulveda with a simple and effective approach to business models and PnLs for startups.
Jason Cohen (again) shares his alternative to business plans: a list of 10 questions to help you figure out where you are and where to go next.
Jason Gorman says small teams win because:
One of the very interesting tools using anger as a driver to innovate is the “You Got Fired!” game.
Get a bunch of managers in a business unit and role-play that their whole team is fired. Indicate also that as a cockup from legal their non-compete clause isn’t applicable.
Then, give them an hour to build a competitor startup that will take revenge on their company on a “That’ll teach them!” mode.
Daniel Gross (a founder I have been following since his first YC startup, Greplin) on ways to decide what company to build on a personal level. I wish he had published that before my last attempt.
A blog post in French by Philippe Silberzahn. A viable project stems from an individual acting on an idea and convincing people.
I have been working at Inch for about a year and a half now, so I thought it was a good time to write a bit about what I do there.
First, a bit of context: Inch is SaaS software for property managers that operates in the French (and Belgian) market. Many of our customers are co-ownership trustees, which is a much bigger deal in France than in most countries, but we also serve other professionals such as rental managers and social housing landlords.
The software can be seen as a mix between a CRM and a ticketing solution. The code base dates back to 2013. The backend is a Rails monolith and the frontend is a React application. There were two other developers on the team when I joined, now we are five, of which I am the most senior. We all directly report to the founder responsible for product, but I am sometimes presented as the CTO to customers because from their point of view I act as such (more on that later).
Now, what do I do there in practice? As you may guess if you know me, a few different things.
That is my main role, and the reason I was hired in the first place. See, our software is used by our customers to manage the relationship with their own customers and suppliers, but they invariably already have pre-existing sotware with overlapping data: their accounting software or ERPs. Some have built them in-house, but most picked from a very fragmented market of industry-specific software which we have to integrate with. So I maintain somewhere between 20 and 30 different integrations with those pieces of software you have probably never heard about.
You may not understand the complexity until you realize that none of those integrations looks the same, because the software itself doesn’t. Some is cloud-based, some is on-prem. In some cases they provide APIs or flat-file data export mechanisms, but sometimes we have to go get the data directly in their database. Of course, we almost never have technical documentation for their data model, and the databases themselves are all different. I have code for over 10 different major DBMS, not all of them using SQL, and not all of them running on typical OSs (hello AS400, hello SCO…).
Add to that integration with a few APIs we use, the maintenance of our own APIs so others can integrate with us and a few custom features for large customers, and you will have an idea of the scope. It is a role that requires domain knowledge, technical insight about many systems and the ability to understand new ones quickly. And maybe most importantly, I have to model our own software and systems so that they can support all of this. If you think about it it’s all about synchronizing data between systems, so I am not all that far from my specialty after all. :)
This role means that when we talk to large customers or prospects I am often the person representing the technical side of the company (hence the CTO thing).
You know the pattern now: I arrive at a startup with a small team of developers where nobody has significant operations experience and I end up becoming responsible for them. It never bothered me, quite the opposite actually: I have been doing systems administration for 20 years and I have always liked it.
In this case, there had been some people who knew what they were doing on the team before, so it was not that bad. For instance, they had a reasonable backup strategy already (yay!).
One of the first initiatives I took was look at spending, which was too high, rationalize a few things and get the hosting bills down. Also, Inch had four main hosting providers for historical reasons: AWS, GCP, Digital Ocean and OVH. The plan was to move everything to GCP, for simplification and compliance reasons. I’m still not done but I got out of Digital Ocean and reduced our usage of AWS by a lot.
We still had three servers left at OVH when their DCs burned last March. We lost two which served as integration gateways, and I took the opportunity to move them to GCP. Losing those servers has made me very busy in the last two months because their IP adresses were all over our customers’ and partners’ config, so I had to get in touch with them all to fix it, since there was sadly no way we could get back the IPs from OVH in time. That won’t happen again now, I used reallocatable IPs this time. The third server will remain at OVH for the time being because it is a very special machine with very specific hardware hosting needs.
At small companies like this, operations also come with a lot of security-related responsibilities. Security is pretty important for Inch, which deals with a lot of personal data. So far, I have worked on improving our operational practices, added a team password manager, rationalized internal auth{n,z} around the GCP tools, and written some documentation. I have also upgraded several important software components and reviewed / fixed all the usual configuration (systems, TLS, CORS, CSP…). I have also started working on the Web application security itself, I have less experience in that space - especially regarding the frontend - than in systems security but I am learning and we have a few experienced Web developers on the team to help.
As usual, nothing is ever done in that space and I still have a lot of work coming up, but at least I believe we’ve significantly improved since I joined.
Finally, the third leg of my role is the same as every developer at Inch: working on the product.
I know the “fullstack” wording makes some people cringe. Of course we all have our favorite part of the stack but I think its is extremely important, especially on small teams, that everyone be able to write or at least understand a feature in its entirety.
With the other two roles taking much of my time, I don’t do as much feature work as the rest of the team but I still try to implement features myself regularly. I had never done frontend stuff seriously before Chilli and it was a different stack so I learn a lot about React and its ecosystem. I do my share of performance and bug fixes too. On the backend especially, I also do quite a lot of architecture and code reviews.
Besides those three main parts of my role, there are of course other things I take part in. Inch encourages its employees to participate in many cross-cutting activities.
An important one is that we all do support in rotation, which means the whole team ends up knowing the customers and identifying the parts of the product that need improvement. This also means everyone can, and does, participate in product decisions.
But that doesn’t stop at product. Inch founders are very transparent about the business, our finances and future plans. Everyone is also encouraged to chime in on strategic decisions for the company.
This article is already long enough so I’ll stop here. I hope that gives you an idea of what I’ve been doing at work since November 2019. If any of this interests you in any way, don’t be shy, get in touch. :)
When I updated my Linux kernel to 5.11 I had the bad surprise to end up with a blinking underscore on reboot. It had been many years since an update had broken my system like that. I fixed it rather easily by booting in rescue mode and downgrading the kernel. I had no time to investigate so I just added linux to IgnorePkg at the time, But I don’t use Arch to run old kernels so today I took the time to fix it “properly”.
To do so, I reproduced the issue, then downgraded again and looked at the logs with journalctl -b --boot=-1. It quickly let me understand that it was GDM that was failing due to something wrong with graphics initialization.
To keep things short, let me skip to the conclusion: if you run into this issue on an old-ish Dell XPS with an Intel Iris Plus 640 graphics card like mine with GDM, Arch and Wayland (or something similar), try enabling early KMS by adding i915 to the MODULES array in mkinitcpio.conf and rebuilding the initramfs, that fixed it for me.
I haven’t posted anything here for 6 months so I thought it would be a good idea to post a personal news update before the end of 2020.
I haven’t posted about it here yet, but my co-founder Julien and me decided to shut down Chilli at the end of last year, in agreement with eFounders. Basically, some of our initial hypotheses about the state of the market we were in (french SMBs) were wrong and the business couldn’t be profitable enough.
In retrospect, it was an interesting experience. I really appreciated being part of eFounders, I do recommend it for people who want to start a B2B SaaS company.
However, I wanted to get out of my comfort bubble too much with this one, by tackling a problem in a market I didn’t know. Because of that, I had to rely on others regarding core business choices, and while that was fine with me initially it ended up being frustrating in the end when things didn’t work well and I couldn’t help much. So if I start another company someday, it will be one where I know the problem domain better.
After we shut down Chilli, I decided to join a small startup editing a SaaS CRM and ticketing solution for property managers called Inch, only available in the French market for now.
I didn’t pick Inch randomly, I have known one of the founders for years since he was a Moodstocks user! Fun fact: at the time, I had met him and he was looking for a technical co-founder for his project. I told him he should learn to code instead and gave him Ruby sample code… and this is why I am back to Rails today. Karma? :)
Anyway, I had been following Inch with interest since its creation, because it is the company I like: solving a real need in a market where tools were either terrible or non-existent. Now they have a unique place in the market and some interesting technical challenges to solve, so I decided to join and help.
And here comes the last piece of news: despite all the madness that happened this year, the biggest change for me is that I am now a father! My son was born last week, and I took a holiday until the end of the year to spend time with him and his mother. I don’t intend to post too much personal news here, but this one deserved it. =)
Yet another Quora answer, this time to with this question which I answered on August 24, 2016:
Why is Transparency a major issue in distributed databases?
First, a few words about what “transparency” is. Transparency is a UX term about the user not noticing that they are using a distributed system. We actually talk about transparencies in the plural, because there are several kinds of transparencies: fault transparency, location transparency, concurrency transparency, access transparency, etc. In my opinion, the choice of wording is not so good: when we talk about “transparency” we actually mean we hide things from the user, so “opacity” would be more logical. (The name comes from the fact that, if we replace a single node system by a distributed system, it will be transparent for the user, i.e. then will not notice it.)
The reason why transparency is important is usability. The more transparencies our system has, the less cognitive burden there is on the user. In other words: transparencies simplify the API of the system.
However, what transparencies we implement or not is a trade-off between that simplicity of API and things like flexibility, performance, and sometimes correctness. Years ago (when Object Oriented programming à la Java was booming) it was fashionable to abstract everything and make the user forget that they were using an actual distributed system. For instance, we had RPC everywhere, which kind of hid the network from the user. Since then we learnt that abstracting the network entirely is a bad idea.
On the other hand, exposing too many knobs to the user is dangerous as well: they might turn them without really understanding what they do and set the system on fire.
So, determining what to expose to the user and what to implement “transparently” is a crucial point in all distributed systems work, not only databases.
In databases in particular, conflict resolution is a contention point. Do we only provide the user with databases that are consistent, knowing that this severly impacts performance and availability? Do we let them tweak the parameters (the R and W parameters in a quorum system, for instance)? Do we tolerate divergence, detect it, inform the user and let them reconcile (à la CouchDB)? Do we provide the user with constrained datastructures that resolve conflicts by themselves (CRDTs)?
Some people have gone as far as saying that Distributed Systems Are a UX Problem and I tend to agree with this line of reasoning.
Continuing my Quora answers series with this question which I answered on June 26, 2012:
How would you explain the concept of a “class” in Python to a 10 year old?
You cannot distinguish the concept of “class” from the concept of “instance”. I think OOP is often taught the wrong way around, paradoxically because it is taught with languages that have support for class-based Object Oriented Programming such as Python (or Java for that matter). So excuse me but I will use another language (Lua, but you don’t need to know to understand) to explain it. (Note: this will be bad Lua on purpose, but the point is not to teach Lua, it is to explain OOP).
Since you wrote “to a 10 year old” let’s proceed with examples.
Imagine you come from another planet and you do not know what cats are. You encounter something small that purrs. You decide to name it Sam. Later on, you see something else very similar, except it is bigger, and you name it Max.
Let us describe Sam and Max in Lua.
sam = {
name = "Sam",
size = "small",
}
max = {
name = "Max",
size = "big",
}
Now we said that they purr, so let’s define purring:
purr = function(self)
print(self.name .. " purrs!")
end
purr is a function that has its first argument called self and represents the thing that purrs. For instance you could make Max purr
like this:
purr(max)
Now you could stop here, or you could see purring as a property of Sam and Max. To represent that we could make purr a “method” of the “objects” Sam and Max:
sam.purr = purr
max.purr = purr
Now with the Lua syntax we cound also make Max purr like this:
max:purr()
Which is a short way to write this:
max.purr(max)
Since we said that max.purr = purr it works as expected.
After some time on Earth you realize there are lots of things like Sam and Max. Moreover there are lots of things Sam, Max and their friends do, such as sleep on keyboards. They also have things in common such as the fact they have two eyes.
You grow tired to say: “Max purrs. Max has two eyes. (…). Sam purrs. (…)”. It would be much simpler to say give a name to the set of Sam, Max and their friends (for instance “Cats”) and say “Cats purr. Cats have two eyes.”.
Note that “Cats” is nothing physical, it is an idea, a category you have created for Sam, Max and their friends in order to be able to express things about them in an easier way.
Let’s switch to Python to show how this is done now:
class Cat:
eyes = 2
def __init__(self, name, size):
self.name = name
self.size = size
def purr(self):
print(self.name + " purrs!")
And how you use it:
sam = Cat(name="Sam", size="small")
sam.purr()
Note that we have never said explicitly that Sam can purr. He can purr because he is a Cat.
In this example:
Cat is a class, ie. a category of objects ;
Sam and Max are instances of the Cat class ;
purr is a method of the Cat class, ie. not much more conceptually than a function that takes a Cat instance as its first argument and some syntactic sugar (ie. special notation) to call it.
By the way, this answer got one of the best comments I ever got on a Quora answer:
Sam and Max are not cats!
;)
From 2011 to 2014, I used to post answers on Quora. I don’t anymore, because I don’t really like what the website has become. I have a copy of some of my answers here but someone commented on one of my answers that it should be available more prominently on the Web, so I decided to repost a few of my answers here, starting with this one.
The original question was:
I’m really new to lua and relatively new to programming.,so kindly excuse if I say something stupid.
I have a table named x and its metatable named y. When I have a
__callmethod defined for the metatabley, then I can callx()but if I have a__callforxthen I can not callx().What is
__callused for? How does it work and what are some examples of usage
I answered it on February 25, 2013.
__call is a metamethod, that means it is meant to be defined in a
metatable. A __call field added to a regular table (x in your example)
does nothing.
The role of __call is to make something that is not a function (usually
a table) act like a function. There are a few reasons why you may want
to do that. Here are two examples.
The first one is a memoizing factorial function. In Lua you could write a recursive factorial like this:
local function fact(n)
if n == 0 then
return 1
else
return n * fact(n - 1)
end
end
Note: this is not a good way to write a recursive factorial because you are not taking advantage of tail calls, but it’s enough for what I want to explain.
Now imagine your code uses that function to calculate the factorials of
numbers from 1 to N. This would be very wasteful since you would
calculate the factorial of N once, the factorial of N-1 twice, and so
on. You would end up computing approximately N²/2 factorials.
Instead you could write that:
local fact
fact = setmetatable(
{[0] = 1},
{
__call = function(t, n)
if not t[n] then
t[n] = n * fact(n - 1)
end
return t[n]
end
}
)
It is an implementation of factorial that memoizes the results it has already computed, which you can call like a function. You can use it exactly like the previous implementation of factorial and get linear complexity.
Another use case for __call is matrices. Imagine you have a matrix
implementation that works like that:
local methods = {
get = function(self, i, j)
return self[i + 1][j + 1]
end
}
local mt = {__index = methods}
local new_matrix = function(t)
return setmetatable(t, mt)
end
You can use it like that:
local M = new_matrix({ {1, 2}, {3, 4} })
local v = M:get(0, 1)
assert(v == 2)
However scientists would probably expect something like this:
local v = M(0, 1)
assert(v == 2)
You can achieve that thanks to __call:
local mt = {
__index = methods,
__call = function(self, i, j)
return self:get(i, j)
end
}
I hope this gives you enough information to understand how you can use
__call. A word of warning though: like most other metamethods, it is
useful but it is important not to abuse it. Simple code is better :)
If you’re like me, you don’t want to depend on your phone to log into a website, and you wish your favorite password manager would support 2FA. Well, it can.
When asked to setup 2FA on a website, get a text code for TOTP. If the website doesn’t give you that option, just use zbar. For instance, with the QR code from the GitHub documentation:
$ zbarimg totp-click-enter-code.png
QR-Code:otpauth://totp/GitHub:LyaLya?secret=qmli3dwqm53vl7fy&issuer=GitHub
scanned 1 barcode symbols from 1 images in 0.03 seconds
Once you get the secret, put it in 2fa/github in pass.
Finally, add this to your .bashrc (or the equivalent for whatever shell you use):
2fa () { oathtool --totp --base32 $(pass 2fa/"$1" | head -n 1) ; }
You can now get your 2FA codes like this:
$ 2fa github
795864
Finally, you can get Bash completion with this simple script, that you must put in /usr/share/bash-completion/completions/2fa:
#/usr/bin/env bash
>/dev/null pushd "${PASSWORD_STORE_DIR:-$HOME/.password-store}/2fa"
l="$(find . -type f | sed s#^./## | sed s#.gpg\$##)"
>/dev/null popd
complete -W "$l" 2fa
All the tools used in that article are available as packages in the Arch Linux repositories.
It was a post on Lobsters that prompted me to post this. Someone from the comments and a former colleague on Twitter told me about a pass extension I didn’t know about which does almost the same thing.
Also, some people think that putting 2FA codes in a password manager defeats the purpose. But in practice TOTP 2FA does not really add much more to the security of my accounts than the strong random passwords I generate with pass. The “second factor” part isn’t really the true benefit.
One actual advantage is that nobody on the network can sniff all of my credentials (like digest-based password verification methods). Another, and I think this is the main one, is that the owner of the website has chosen part of the credentials and hence ensured some degree of strength. What I do preserves both of those properties, so I’m fine with it. By the way, note that password managers like 1Password and Passbolt do the same thing.
One thing you can do to improve the security of the whole thing is use 2FA to access pass by storing the GPG key in a Yubikey.
A few days ago I read this article which made me want to list some of the tools used at the four startups I have worked at so far.
I have mostly listed SaaS tools here, but you can also check out the ~stack tag on my Pinboard for more.
I have also added a section at the end for some tools I use for my own projects.
You know how some discussions can make you pause and introspect for a while after they happen? Well, I have had such a discussion recently about how strong my opinions are nowadays, and I decided to write something about it.
People who have spent significant time around me know all too well how I behave when I get interested in a topic. I can spend weeks (and sometimes even much longer) reading all I can find about it until I know its ins and outs. This results in me being able to discuss and have opinions about things which are apparently completely random, but which I ended up picking up along the way for some reason.
For the longest time, I have believed in strong opinions (somewhat) weakly held, and for the most part I still do. In particular, I agree with the “somewhat” part, which says that the strength with which you should hold an opinion depends on how you formed it.
What has changed in the last 5-7 years is my view on how explicit those opinions should be, and how much I should fight for them.
I was strongly influenced by the Open Source community of the early 2000s where, if you wanted something to happen, you had to state your opinion and be ready to defend it - sometimes ferociously - with arguments. If you didn’t, you would be ignored.
What outsiders often overlook about this method is that it worked, and it was pretty efficient. Most people got used to it, and in general it did not turn into endless debates, because in the end we had benevolent dictators to settle the matter.
But a problem I could not see for a long time is that this excludes a lot of people from the discussion. People who, because of their education or the position society put them in, won’t express their own opinion if it contradicts the current consensus or that of someone they either respect or fear; or people who won’t engage in anything remotely resembling conflict because that’s not in their nature.
It is especially easy to ignore the existence of these people when the only way you are communicating is through asynchronous text over the Internet. However they often have very interesting things to say, with different points of view that can make headway or prevent big mistakes.
There are lots of ways to work around that issue, including timeboxing contributions on a topic while keeping them secret, or simply having people in positions of power and people more comfortable with their opinion express it last.
Another matter is how much you should fight for your opinion. I am not talking about the famous XKCD comic, I know I can be this guy sometimes, but that is something else.
Some time ago I was struggling with how to handle disagreement at work, and I became an adept of the way Amazon does things, its Leadership Principles, and in particular Disagree and Commit:
Leaders are obligated to respectfully challenge decisions when they disagree, even when doing so is uncomfortable or exhausting. Leaders have conviction and are tenacious. They do not compromise for the sake of social cohesion. Once a decision is determined, they commit wholly.
When organizations follow this principle, it helps a lot with the issue, because it makes it clear that you can - and should - express dissenting opinion, and how you can align on things you do not agree with without making it look like you changed your mind when you did not.
(On a side note, I even think in some cases making dissent mandatory by instituting a Tenth Man / Devil’s Advocate rule can be beneficial.)
However, it turns out this principle is more complicated than it sounds, and there are two important points I did not immediately understand, which are much more explicit in Bezos’ 2016 letter to shareholders.
The first is that the person who “disagrees but commits” is not necessarily the subordinate in a power relationship. Bezos says:
This isn’t one way. If you’re the boss, you should do this too. I disagree and commit all the time. We recently greenlit a particular Amazon Studios original. I told the team my view: debatable whether it would be interesting enough, complicated to produce, the business terms aren’t that good, and we have lots of other opportunities. They had a completely different opinion and wanted to go ahead. I wrote back right away with “I disagree and commit and hope it becomes the most watched thing we’ve ever made.” Consider how much slower this decision cycle would have been if the team had actually had to convince me rather than simply get my commitment.
The second point I missed is the meaning of the sentence “they do not compromise for the sake of social cohesion.” The “compromise” part is clear enough; here compromising would have meant giving the green light but with, say, fewer resources. From experience compromises like this often end up poorly. But the hard part is “for the sake of social cohesion”. Here is what Bezos has to say:
Note what this example is not: it’s not me thinking to myself “well, these guys are wrong and missing the point, but this isn’t worth me chasing.” It’s a genuine disagreement of opinion, a candid expression of my view, a chance for the team to weigh my view, and a quick, sincere commitment to go their way.
Unlike Bezos my current (weakly held) opinion is that sometimes keeping social peace is worth not engaging in some minor issues. Some people will always appreciate being told when you think they’re mistaken, but others won’t even if they end up admitting they were wrong in the end, so it only makes sense to contradict them if it is worth risking being resented for it.
The first step for all this to work is probably to admit that opinions can exist without being “right” or “wrong”.
That may be obvious to you but for a long time it was not for me. When I was a kid I looked at things in binary: true or false, right or wrong, better or worse. This made me enjoy CS and math, but paradoxically learning more advanced math (partial orders, Simpson’s paradox, Gödel’s incompleteness theorems…) showed me that even in the hardest of sciences things were more nuanced.
Anyway, there are reasons why people come to hold a set of beliefs which makes sense at least locally. Learning about this background is important, whether it helps you convince them or changes your own mind.
Don’t worry though, I still hold a few strong opinions, both in my field (some quite strongly - you won’t easily make me change those for instance) and outside of it!
In projects that use SQLAlchemy and Alembic via Flask-Migrate, you may want to truncate the migrations history. By that I mean: rewrite all the migrations up to some point as a single initial migration, to avoid replaying them every single time you create a new database instance. Of course, you only want to do that if you have already migrated all your database instances at least up to that point.
As far as I know, there is no Alembic feature to do this automatically. However, I found a way to avoid having to write the migration by hand. Here is an example of how you can achieve this with a project using Git, PostgreSQL, and environment variables for configuration.
First, checkout a commit of your project where the first migration you want to keep is the current migration, and create a temporary branch. Then, take a note of the ID of that migration (for instance abcd12345678), delete the whole migrations directory and reinitialize Alembic.
git checkout $my_commit
git checkout -b tmp-alembic
rm -rf migrations
flask db init
At this point, using Git, revert changes to files where you should keep your changes, such as script.py.mako and env.ini. Then, create a temporary empty database to work with.
git checkout migrations/script.py.mako
git checkout migrations/env.py
createdb -T template0 my-temp-db
Now create the initial migration that corresponds to your model, with the ID that you noted previously, e.g.:
MY_DATABASE_URI="postgresql://postgres@localhost/my-empty-db" \
flask db migrate --rev-id abcd12345678
Finally, you can delete the temporary database, commit your changes to your temporary branch, merge it into your main development branch and delete it:
dropdb my-empty-db
git commit
git checkout dev
git merge tmp-alembic
git branch -D tmp-alembic
This article is out of date. Arch Linux stopped shipping OpenSSH with socket activation due to the risk of DoS attack. Now you can just set Port in sshd_config as usual.
I often change the default SSH port from 22 to something else on servers I run. It kind of is a dangerous operation, especially when the only way you have to connect to that server is SSH.
The historical way to do this is editing sshd_config and setting the Port variable, but with recent versions of Arch Linux and the default configuration, this will not work.
The reason is that SSH is configured with systemd socket activation. So what you need to do is run sudo systemctl edit sshd.socket and set the contents of the file to:
[Socket]
ListenStream=MY_PORT
Accept=yes
where MY_PORT is the port number you want.
I hope this short post will avoid trouble for other people, at least it will be a reminder for me the next time I have to setup an Arch server…
In my last post I told you I had plans that I was not ready to talk about yet. Well, the time has come. I am happy to announce that I am now the CTO and co-founder of a startup called Chilli.
Chilli is not a typical startup, it is an eFounders project. You may know eFounders as the first startup studio in France, which originated companies such as Front, Aircall and Spendesk. The way they usually work is that they identify a problem that needs solving and find founders to tackle it, providing them both support and funding in exchange for equity. When the studio was created, I had doubts about the model, but later on I became quite enthusiastic about it.
Most eFounders companies are Software-as-a-Service businesses, and several of them were born of a need identified in traditional SMBs and SMEs. However, many pivoted to serve a different market, either tech companies or enterprises, and we can see the same pattern in other SaaS companies as well. So we end up with software that doesn’t sell in the market it was originally designed for, and SMBs left on the side of the road with unaddressed digital needs. The reason, we believe, lies with the SaaS-to-SMBs distribution model, and that is the issue Chilli intends to solve.
We are certain that the solution to that problem must involve software. However, we also think technology alone will not be enough; a human touch is necessary, which is why my co-founder and CEO Julien comes from a consulting background. What we will build is a hybrid platform to help leaders identify the pain points in their companies and match them with the best digital tools to solve them. By starting from the customer’s needs, we will work around the distribution cost issues and become the missing link between SaaS vendors and traditional SMBs and SMEs.
For me, this is a new and exciting challenge. Despite having been a very early employee at startups twice, I have never been a founder yet, and it is something I have wanted to do for a while. Moreover, it means I will be doing a lot of Product and Web development again, which will change from the last five years I spent mostly in the world of systems software in C and Lua.
On that note, our Web stack is (typed) Python 3 / Flask and TypeScript / Angular, and I am looking for a full stack developer to join the team. This is a junior to mid position based in Paris, France (no remote); since most of the work is on the frontend experience with Python is not a requirement. If you are interested, get in touch.
You may have heard it already: five years after I joined Lima, the company is shutting down.
Obviously, things did not go like we hoped they would. Customers are disappointed, and wondering what will happen now. Let me try to answer some of the questions I read online the best I can.
Please note that this is my personal take on things, and does not represent the views of anyone else but me (i.e. not Lima, not other employees…).
Lima as a company no longer exists. It ran out of money. Its employees (including me) have all been fired, and its assets will be sold to pay its debts.
Regarding why the company died, it is a long story and it is not my place to tell it all. What I can say is that it ran into unexpected funding problems in early 2017, shortly after we started shipping the Lima Ultra. During most of 2017, there was strong demand for the product but we could not fulfill it because we did not have enough cash to pay for production and shipping (Remember the never-ending waiting list?) At the end of the year, we had to fire a large part of the team and we switched our business model to sell our software to other companies. We made a deal where we worked for another startup. The deal was good enough to keep the company afloat and the product alive for a year, but it forced us to stop selling Lima devices. What happened recently is that this deal eventually fell through, leaving us with no viable options.
This past year was not the best time of my life, or for any of the other employees who stayed. Many of us could have left for much better jobs at any time, some did and I cannot blame them. All those who stayed on board all this time did so hoping for a better end for the company and its customers.
Once Lima’s servers shut down, Lima will keep working on your local LAN with the devices you have already paired with it. However, a lot of things will stop working.
First, it won’t be possible to add new devices to the system. That’s because, when you log a new device into Lima, you do so with an email and password. To find out which Lima those credentials belong to, the system asks a server, and that server won’t answer anymore.
Second, it won’t be possible to reset your password, because email confirmation will be broken. If you have forgotten your password, change it now while the servers are still up.
Third, the sharing feature will be broken, because it relies on sending HTTP requests to relay servers which will go down as well.
Finally, it won’t be possible to access Lima from outside your home. This is a little harder to explain than the rest. Basically all communications between anything related to Lima (Lima devices, your devices, servers…) happen in a peer-to-peer VPN. To “locate” devices within the VPN (basically figure out how to talk to something), devices rely on a node which is called the ZVPN master. The IP address and public key of that node are hardcoded into every Lima client, and that node will go down as well. The use of that node is not needed on local networks because Lima devices and applications have a protocol to pair with other devices associated to the same account on a LAN without talking to any server.
At that moment, not that I know of. Your data was never stored on Lima’s servers, and all data traffic going through relay servers is end-to-end encrypted, which means that even if an attacker took control of one they couldn’t decipher your data.
However in the long run there are two issues.
First, we won’t be able to publish updates for the Lima firmware and applications anymore. If a security issue is found in one of the components they use, they may become vulnerable with no way to fix them.
Second, if someone was to acquire all the assets or Lima, including the domain and code signing certificate, they could theoretically do everything Lima was able to do, including publishing updates. That means they could publish malicious updates of the applications and firmware.
That second issue sounds scary but I do not think there is any chance it will happen. Potential acquirers will probably be more interested in Lima’s technological IP, there are very few chances that an acquirer will get all the assets necessary for such an attack, and even if they do they probably won’t have an interest in performing it. Even if it did happen, it would be easy to notice. Still, I have to mention it for transparency.
What I will personally do now, and what I advise users to do as well, is export all my data out of Lima, unplug the device and uninstall all the applications.
Note: If you have problems when trying to recover your data (due to e.g. a hardware issue with the USB drive), do not uninstall the applications. The data on your desktop might sometimes help recovering some of the files.
If you have an issue with the Decrypt Tool, check here for potential answers.
EDIT (2022-02-06): What I feared when I wrote this post did happen a while ago. Someone did get hold of the meetlima.com domain and uses it to host what is clearly a scam. They want you to think they are us, but they are not. I do not think they have performed any attack on the devices themselves, but it is definitely not safe to keep using the original software anymore.
It depends on the users. I don’t know anything that is exactly like Lima. There was Helixee, which I have never tried out, but I just found out they are shutting down as well. I also learned that a project I had never heard about before called Amber had a special offer for Lima customers.
For technical people, you can probably do most of what you were doing with Lima with a Synology NAS, or a setup based on some small computer and Open Source software such as Nextcloud or Cozy Cloud.
However, Lima was never designed for technical customers. It was built for, marketed to and mostly bought by non-technical people. For them, I don’t have a good answer. I heard that WD My Cloud Home had become a lot better than it once was, but I have not tried it personally.
To the best of my knowledge, there is no way that can happen. This makes me extremely sad, especially since I know there are parts of the code I would love to reuse myself, and that could be useful to other projects.
The reason why we cannot open-source is that the code does not belong to us, the employees, or the CEO. Intellectual property is considered an asset of a bankrupt company, and as such will be sold to the highest bidder to pay the company’s debts.
That being said, Lima has contributed some source code to a few Open Source projects already. Most importantly we fixed the issues in OSXFUSE that prevented it from being used for something like Lima, and those fixes are now in the main branch.
Completely independently from the company, the former CTO of Lima has also released a project which looks a lot like a second, fully decentralized iteration of the Lima network layer ZVPN (using a DHT instead of a master node, and WireGuard instead of TLS). Let me be clear: this project contains no code or IP from Lima, it is a clean room implementation.
For Lima Original, no, I think that would be impossible (or rather, I can’t see a solution that doesn’t involve soldering…). The device is not worth much today anyway, its specs are so low I don’t think you could run any other private cloud software on it.
For Lima Ultra, a few of us ex-Lima employees (and the CEO) are trying to figure out a way to let users get root access. We can’t promise anything, but we will keep you informed if we do.
EDIT (2019-02-18): We did it, check this out!
Some people have mentioned that what was happening was not in line with what had been said in the Kickstarter FAQ.
This FAQ has been written in 2013, before I or any other Lima developer joined the company. At the time Lima was a very small project with two founders trying to raise $70,000 to make their dream happen. Instead they raised $1,229,074, hired 12 people (including me), and the rest is history.
I do not think we have not communicated like that ever since, especially regarding decentralization. As far as what I know we have been transparent that our servers were needed for some major features of the product, as it was obvious the few times they went down. You may ask why we didn’t amend this page then, and the answer is (I think) that it is technically impossible to edit it after the campaign is over.
Regarding Open Source, I sincerely believe the CEO of Lima would have done it if it was possible, but with the success of the Kickstarter the company had to take VC funding very early on (see below), and from that moment on I do not think it was in his hands.
OK, let’s address this last. What Kickstarter money?
Yeah, the founders raised over a million dollar. But do you remember how much the backers paid for those devices? From $59 to $79 each. Well, as bad as the hardware was, it was planned for about 1000 devices, not over 10,000. And it was pretty expensive.
I don’t know the exact figures, but basically Lima did not make money on those devices, or no significant amount of money at least. Which is why it raised extra cash from VCs just afterwards, to pay the team that worked on the project, the production of more devices to sell, etc…
If you still think something shady went on with that money, rest assured: when a company like Lima goes bankrupt, its books are closely investigated by the state, which is one of its main creditors. So if you are right, the people responsible will end up in jail. (Spoiler: I really don’t think it will happen.)
Yes, I have plans.
No, they are not in any way related to Lima.
I will tell you more next month, probably.
This is just a short post to share what I now consider, after 10 years in the industry (and almost twice as many writing code), my core software architecture principles.
You may or may not agree with them all, but if you design software or systems, you should have a similar list in your head; it really helps a lot when making decisions.
Without further ado, the principles are:
Separation of Concern often trumps not repeating oneself (DRY). In other words, avoiding duplication does not justify introducing coupling.
Gall’s Law: “A complex system that works is invariably found to have evolved from a simple system that worked.”
Conway’s Law: “Organizations produce designs which are copies of their communication structures.”
When writing code or designing, stop and think “consequences”. What will be the impact of what you are doing on the rest of the systems? Could there be adverse side-effects?
Think about debuggability in production. There is nothing worse than having your software break and not being able to figure out why. Do not automate things you do not understand.
Shorten feedback loops. (added on 2025-08-21)
If you are like me, maybe you have recently updated your Linux laptop and found out that right click had stopped working on the touchpad. It took me half an hour to figure out why. I was looking at low-level stuff until I realized libinput debug-events saw the right thing:
event11 POINTER_BUTTON +3.35s
BTN_RIGHT (273) pressed, seat count: 1
event11 POINTER_BUTTON +3.49s
BTN_RIGHT (273) released, seat count:
It turns out Gnome decided to change the default behavior of touchpads in version 3.28:
All touchpads now use a gesture for secondary click (the equivalent to right click on a mouse) by default. To use the gesture, keep one finger in contact with the touchpad and tap with another finger. In many cases this replaces tapping areas of the touchpad as the default secondary click method. A choice between the two behaviors is available in the Tweaks application.
Of course the new behavior is terrible if you use physical clicks, plus you have to re-train your muscle memory… To revert to clicks in the bottom-right corner, you can use the gnome-tweaks application as advised, or simply this gsettings command:
gsettings set org.gnome.desktop.peripherals.touchpad \
click-method areas
But seriously, Gnome developers, if you are going to change an important default like that, why not at least add an option in the obvious place where it belongs?

Today marks the fourth birthday of my joining Lima, and it is a good occasion to talk a bit about some of the things I have been doing there.
For the public, Lima makes a device which turns a USB hard drive into personal “cloud” data storage. We actually shipped two devices, Lima Original in July 2015 and Lima Ultra in Decemner 2016.
Technically, Lima is really a personal distributed filesystem. Every single machine running a Lima application logged into to the same user account is a node in the system. All nodes keep a copy of all the filesystem metadata, which means the user can always browse the whole file tree, create or remove files, move them around, etc. The actual data, however, may or may not be on the device, which makes it possible to access a multi-terabyte filesystem on a mobile device. In theory, the Lima device is just another node in the system, except it always stores all the data.
To make this all work, we need a mechanism to keep the filesystem metadata synchronized on all nodes. I have designed and implemented the algorithm and protocol behind this. It is tricky, because we always want users to be able to write to their filesystem, even on an offline device, so when devices come back online there may be conflicts to resolve. It is sometimes tempting to do something simple and correct, but which would not result in what the end user would expect. Yes, distributed systems are a UX problem…
I can’t get into too much detail here, this being proprietary technology and all, but I have drawn a lot of inspiration from CRDTs as well as older systems like Bayou and WinFS.
Like I said, in theory, the Lima device just runs a slightly modified Lima application. And in theory, theory and practice are the same thing. But in practice…
The Lima Original hardware was very limited, with a slow exotic CPU, 32 MB of RAM and 8 MB of Flash. At some point we realized that we would never succeed in running our software on it with reasonably good performance, so shortly before shipping we decided we had to do something radical: write entirely different software for it. We called that software “minicore”, and I wrote its first version in a 6-week rush.
Unlike the Lima filesystem, minicore is designed to be very conservative about its use of memory and I/O resources. It uses a different data model and synchronization protocol, which relies on client machines running Lima to do the heavy lifting. It prioritizes what Lima devices have to do best, which is downloading or serving file data.
The trade-off here was a huge increase in the complexity of the system. Minicore itself is not that complex, but we had to introduce a separate protocol between client nodes and Lima devices, and a third protocol to deal with replication between Lima devices (if you add several Lima devices to the same account they mirror one another).
I won’t get into too much detail about all the other things I have written or worked on over the last four years, but important ones include:
Lately, my role has changed as I have become responsible for software architecture for the whole company. I now work more closely with the developers outside of my team (Core Engineering), who implement the user-facing parts of our stack. I also do some hiring - and by the way, if you are interested, get in touch. :)
All in all, it was four interesting years. Things have not always been easy to say the least, but a relaxing job is not what I expect from a startup anyway. We have some really great people on the team, challenging plans for the future, and we learn a lot, so here’s to the next years of Lima!
A few weeks ago I was debugging network code, and I needed to check if some
sockets were bound with SO_REUSEADDR and/or SO_REUSEPORT. I researched how
I could inspect that, and was surprised to find out that the Linux kernel did
not expose those options to userland.
Thankfully, there is
a kernel module called knetstat
which lets you do this very easily by adding new files into procfs. When I
found it it only supported TCP, but I needed the information for UDP as well so
I contributed the code to
support it.
If you write networking code on Linux or just run servers, I encourage you to
check it out. It is a nice little tool that can come very handy in some
situations, including when you need to check options like SO_{RCV,SND}BUF.
This post is a rough transcription of a lightning talk I gave at dotScale 2017.
EDIT (2024-11-24): this post is on HN today. For people finding this now, Lima has shut down since then.
One of the things I work on at Lima is master-master filesystem replication. In this kind of system, we need to track causality. In a nutshell, given two events modifying a given piece of data and originating from different nodes in the system, we want to know if one of those events could have influenced the other one, or in other words if one of those events “happened before” the other one.
To do that, we use constructs such as Version vectors. The idea is that we give each node in the system a globally unique identifier, and we associate it to a counter.

When an event modifying data on a node occurs, we increment the local value of the corresponding counter by one.

Version Vectors are partially ordered. Given two vectors, if we can find one such that, for every node, its counter is higher than the other one, then we say it descends the other one, meaning the related event “happened after” the other one. Otherwise, we say that the vectors are concurrent, and typically that means we will probably have some kind of data conflict to solve.

When we merge data changes we also merge the vectors, and to do so we take the maximum value of the counter for every node.

This works fine in most cases, but there is one case where it breaks down: highly dynamic systems experiencing a lot of churn. This means systems where nodes join the system, modify some data, then leave forever. The issue with such systems is that, even though there may not be a lot of active devices at any given point in time, the number of unique node identifiers in version vectors keeps increasing. We call that issue actor explosion.
Interval Tree Clocks are an attempt to solve this problem. Instead of
giving a unique identifier to every node in the system, we take the
real-valued interval [0, 1] and attribute a part of it (not necessarily
contiguous) to every node. On top of it, we draw an integer-valued curve.
We call the combination of the interval share and the curve a stamp.

To add a new node to the system, we start from an existing node and we fork it, meaning we give part of its share of the interval to the new node.

When an event occurs on a node, it increases the height of the curve of its copy of the stamp anywhere within its share of the interval. Comparison works similarly to Version Vectors: if the curve of a stamp is above the other one, it descends it, otherwise the curves intersect and the stamps are concurrent.
When a node wants to leave the system, it merges back with any other node and surrenders its share of the interval. To merge, we just take the maximum curve.

The beauty of this scheme is that a node only has to know about its share of the interval, not information about all other nodes. There are no globally unique node identifiers.
If we choose how we increase the height of the curve when an event occurs in a clever way which I will not detail here, we can ensure the complexity of the curve remains low. We can then encode it efficiently using a tree-shaped data structure and a custom binary format, with a size that depends more on the number of nodes interacting with the data at a given point in time than on the overall number of nodes which have touched it since inception.

If you want to know more, I encourage you to read the 2008 paper by Paulo Sérgio Almeida, Carlos Baquero and Victor Fonte; it is one of the best I know on the topic of causality. You can also check out my Lua implementation of ITC or one of the other implementations linked in the README.
EDIT (2024-11-24): other interesting resources are this slide deck by Carlos Baquero and those posts by Fred Hebert.
In November 2013, I gave a talk about the Lua Ecosystem at Lua Workshop, in which I mentioned that finding the best module for some job could be hard and introduced a website I had built to solve that problem.
That website, Lua Toolbox, was inspired by the popular Ruby Toolbox. It had two major features: modules classification (“labels”) and endorsements by users. I had written it using Leaf Corcoran’s relatively new Lua Web framework, Lapis, and Redis as the datastore.

In August 2014, MoonRocks, one of Leaf’s many projects, became the official website for LuaRocks. It offered many features including user accounts, manifests and the ability to upload rocks on the Web. We quickly started to discuss merging Lua Toolbox into MoonRocks, but it led nowhere at first and the two websites continued their separate lives.
Eventually, in March 2016, Etiene Dalcol took up the project. By September her and Leaf had written the code for labels and “follows”, which replace endorsements. They also added a feature for users to convert their Toolbox endorsements to LuaRocks follows. I sent them a data dump which they imported into the production LuaRocks website, and I turned Lua Toolbox read-only.
Today, I finally replaced the whole website by a static page redirecting users to LuaRocks. In the end, the Toolbox had 232 users and referenced 1217 modules. The source code remains available but I would not advise you to do anything with it (it uses old Lapis APIs, from a time where plain Lua support was not official).
Alice Goldfuss started a debate about having developers on call. I am firmly in the camp of those who think it is a good thing, probably because I was a sysadmin before being a software engineer. But this means those developers must have the power to refuse to ship broken code that may make the pager go off as well.
In fact, it is all about fostering the sense of ownership, which is the N.1 trait I look for in developers. And this won’t be the first time I have quoted great people saying similar things!
Here is something people who haven’t worked in B2B software companies may not understand: sometimes knowing your product’s metrics, how its users behave and the market it is operating in can be more valuable than the product itself. Small B2B2C software vendors often end up knowing things about their (huge) customers’ clients that the customers themselves don’t.
What that means is: if you provide third-party software to your users, ask the third-party vendor what data points they collect, what they can do with them, and if they can feed them back to you. And if you are the third-party vendor, consider the value of that data, and maybe find clever ways to turn it into a competitive advantage.
Explaining why AI makes decisions is going to be very important in the years to come. Trusting algorithms is hard for humans. This slows down adoption and progress the most in areas that really matter. So we need a way to let concerned people “feel” why machines do things the way they do it. Because writing papers is nice, but we all know simply telling people to trust experts is never going to work. This means those topics are now also UX problems.
Someone made a nice web page to answer a question we hear pretty often. :)
EDIT: That was about cantyoujust.no which does not exist anymore.
Here is something I have been believing in for a long time: there is no good technical reason why most Web-based application vendors force you to store your data on their servers. It should be possible to use, say, a presentation application from Google to edit slides stored on Microsoft’s servers.
Sadly, for reasons that probably have a lot to do with business models, things like remoteStorage did not really take off. But I hope someone will revisit that idea someday, and find a way to make it take off.
I have not found the motivation to write here for a while, so I will be trying something new. This is the first of a series of posts which will be composed of a few smaller unrelated snippets, mostly things I would usually post in a smaller form on Twitter. They will relate to anything I will have found interesting recently. Some will be practical and some will just be reflections about a given topic.
If, like me, you are mostly a Linux user but sometimes have to work on a Windows machine, have a look at MSYS2. It allows you to quickly get a decent environment set up, with Bash and a package manager, (pacman), which you can use to install things like Git.
I have had my Jolla C for two months now. I have not spent as much time as I would like playing with it, but so far I am pretty happy with Sailfish. Like WebOS, it relies heavily on gestures, which makes most of the features of the phone usable with the thumb of a single hand. There are not many applications in the store so far but the default applications are good in general and most Android applications work well, I could run Lima on it without any problem for instance.
From a programmer’s point of view, it is all I hoped for. I went from zero to Hello World on the device in less than an hour. Coding happens in QT Creator and the UI of the application is described with QML. The SDK ships with an emulator that runs in VirtualBox, but also with a second virtual machine which is used to compile code, which means you don’t have to install a cross-compiler on your local machine. Applications, which are technically RPM packages, can be deployed to hardware devices via USB or SSH over WiFi.
It also has to be noted that the native terminal application is very good, much better than anything I saw on Android. A SSH client is included so it can occasionally be used to log into a server.
The main reason I have not made that device my main phone yet is that its battery life is much worse than that of my Android phone, but hopefully that is something that can be solved.
I stumbled upon an insightful tweet about software development time estimation. The larger the tasks we try to estimate, the more wrong we will be. Maybe an estimate as a single number representing time is not enough; maybe we should also try to figure out the fractal dimension of the job to be done.
Everyday I grow more convinced that this is true. Not only of failure conditions, but even more so of things like reconciliation strategies in case of causally concurrent events, for instance how to represent data with CRDTs.
It is a problem, because people doing distributed systems often either do not want to do UX, do not have the competence to do it, or just do not have the permission. We tend to be at the bottom of the stack, and some people think it would be a disaster to let us work on anything user-facing. On the other hand, most people actively doing UX do not understand or care for distributed systems issues.
The result of this is that, if we design without taking technical constraints into account, we end up with systems that cannot be implemented without relying on central authority. Add to that the fact that it is almost always much easier to implement something in a centralized way, and you have one reason why the Cloud is winning and distributed alternatives to popular services never seem to take off.
I stumbled upon two fantastic documents related to Palm recently. The first is a series of blog posts called A History of Palm. Very interesting, even though I disagree with the conclusion of the fifth post. What killed the Pre line at HP was not that WebOS did not take off; they never let it a chance to. I have been using a Pre 3 for a long time (I had pre-ordered it so I was one of the lucky few to have it in France) and it was a fantastic device, extremely influential on future evolutions of both iOS and Android (after all, the interface was the brainchild of Mathias Duarte). What ended Palm’s legacy was the eviction of Mark Hurd and his replacement with Léo Apotheker, who hates hardware. Probably one of the worst mistakes the HP board ever made.
The second document is the Zen of Palm, a design guide for Palm OS from 2003 which is still pretty relevant today in some ways.
Two decades years ago cooperative scheduling was more frequent than preemptive scheduling. Windows 3.1 was completely cooperatively scheduled, which means that all applications had to yield to the system scheduler, or they would block the whole system. In Mac OS 9, the threading API was cooperative as well within a process, even though different applications would be scheduled preemptively by the kernel.
Modern OSs do not make much distinction between a thread and a process anymore, threads are basically processes which share memory, and they are scheduled preemptively. I have come to think that this design results in code of lower quality, and which is harder to reason about. Of course, it is still possible to use event loops or programming languages which do their own preemptive scheduling (like Lua)…
At Lima, we use the LMDB database extensively, and we looked into its source code in order to debug a few issues we had. To help us diagnose broken databases, and also because I wanted to learn more about it, I wrote a parser for the LMDB database file format in Lua in my spare time, which I published under the Open Source MIT license.
In this post, I will give an overview of how a data.mdb file is structured. Note that I will sometimes skip over some things and operate under the assumption that you are running on a little-endian platform with 4 kB pages, etc.
The first thing to know is that the data.mdb format is platform-specific,
which means that you cannot necessarily open a database created on a machine
on another one. In practice, on ARM and x86, the only thing you have to care
about is whether the machine is 32 or 64 bits. On a 64 bit machine, you can
read the data from a 32 bit database by exporting it to a text format using
the mdb_dump utility compiled in 32 bits and reimporting it with a 64 bits
mdb_load. With lua-mdb, you can read a 32 bits database directly by passing
the bits option to the constructor.
A data.mdb file is made to be mapped into memory, and as such is organized into a set pages of 4 kB. All pages start with a header which contains the page number and some flags which determine the type of the page. For all page types except overflow pages, the header also includes the bounds of the free space in the page.
The first two pages (page numbers 0 and 1) are called meta pages. Meta pages
start with a magic number (0xBEEFC0DE), a version (currently 1), and the
address and size of the memory mapping. The address is only valid if the
mapping is fixed,
i.e. if LMDB is used with the option MDB_FIXEDMAP.
After that, we find two structures describing what LMDB calls databases. The first one is the “free” database, which is used to track free pages, and the second one is the “main” database.
Finally come the number of the last page used in the file and the ID of
the last transaction that wrote to the page successfully. Indeed, those meta
pages is the mechanism LMDB uses to implement
MVCC.
There can be a single writer at a time, and it will always write to page ID
N % 2, where N is the current write transaction ID. Meanwhile, readers will
read page N % 2 + 1, providing readers - writer isolation. This means that,
when opening a .mdb file, the latest data can be found in the databases pointed
to by the meta page with the highest transaction ID.
As we saw, a meta page contains two structures describing databases. Databases in LMDB are technically B+ trees. The structures contain some environment flags used at runtime, information about the tree including its depth, the number of pages of each type and the total number of entries, and finally the page number of the root of the tree.
Pages of the tree itself can be one of three types: branch pages, leaf pages
and overflow pages. There are two other types (BRANCH2 and SUBP) which are
only used with specific DB options (MDB_DUPFIXED and MDB_DUPSORT) and that
we will ignore.
Branch pages represent internal nodes of the tree. After the header, they contain an array of “pointers” (technically, 16 bit indices within the page) to “branch nodes” within that page. The number of branch nodes, which is the number of keys in the page, can be deduced from the boundaries of the free space in the header.
Branch nodes are allocated from the bottom of the page up. They start with a page number, then a key prefixed by its size. Branch node pointers are sorted according to the key and the current key comparison function, and the page number indicates the page which should be visited to look up keys between the key of the node (included) and the key of the next node. That page can be a branch or a leaf.
Like branch pages, leaf pages start with a number of pointers to leaf nodes. Leaves store the actual keys and values contained into the DB. Small values are stored in the leaf node itself and large values are stored in overflow pages.
Leaf nodes are slightly more complex than branch nodes. They contain flags, which determine if the value is contained in the node or in an overflow page, and the key which corresponds to the node. For small values, they contain the value itself, and for larger values they contain the page number of the corresponding overflow page.
Large values are stored in so-called overflow pages. Those pages are pretty simple: after the header, they contain the raw value (not prefixed by its size). This value can be larger than the size of a page, which explains the name: they overflow on later pages. Note that this means those later pages do not start by the usual header.
I have explained the basics of the data.mdb file format. Should you have to debug broken databases, it will help you get started. If you need more, read the lua-mdb source code or the LMDB source code (it is only about 10000 lines, although sometimes not easy to understand given that its author adopts a style that favors performance to readability).
It is that time of the year where my RSS feed fills with book recommendations, so I decided I would try my hand at the exercise this year and write a short paragraph about five books I read in 2015 that left me with a good impression.
Scott Berkun was a management writer and consultant when he was hired by WordPress. Before that, he had been a UX designer and team lead at Microsoft, on the Internet Explorer project. When he joined WordPress, the company had over 50 employees, it was completely flat and had a culture of remote work. The founder wanted to experiment with a team-based structure, and Scott was to be the first team lead.
The book is not a story about how WordPress was a doomed company, and how Scott came in to save the day. On the contrary, WordPress was already working much better than most companies that size, and Scott was part of a series of changes they implemented to scale to the next level without losing too much of their culture.
That culture, rooted in Open Source and documented by Berkun, an outsider, is what makes the book interesting. I would say it is a must read for anybody considering starting or leading a team at a small software company.
This book is somehow similar to the one above, but on a larger scale. Jim Whitehurst is the current CEO of Red Hat, a company with over $1.5B in yearly revenue, over 8000 employees, and a culture firmly rooted in Open Source (I started using Linux with their distribution, Red Hat 6.2, in 2000).
Before joining Red Hat, Whitehurst was COO of Delta Airlines. When he arrived at Red Hat, he quickly realized it was a company that worked in a very different way, following the core values of Open Source, transparency and meritocracy (Whitehurst uses the word with the meaning it had in the Free Software community in the 90s and 00s, without the background that makes it controversial nowadays).
The book is both analytical and prescriptive, and it shows how the way Open Source projects are organized can be successfully adopted by a large company.
A 1975 classic. Gall is a (retired) pediatrician, but he is well-known in software engineering circles for this book which contains, among other things, what is now known as Gall’s Law:
A complex system that works is invariably found to have evolved from a simple system that worked. A complex system designed from scratch never works and cannot be patched up to make it work.
The book is a curious mix of humor and philosophy, written in faux-scientific style and full of things which people working in Distributed Systems will be painfully aware of:
In complex systems, malfunction and even total non-function may not be detectable for long periods, if ever.
Any large system is going to be operating most of the time in failure mode.
Intermittent failure is the hardest case.
One does not know all the expected effects of known bugs.
In setting up a new system, tread softly. You may be disturbing another system that is actually working.
Bad design can rarely be overcome by more design, whether good or bad.
A system that ignores feedback has already begun the process of terminal instability.
In dealing with large systems, the striving for perfection is a serious imperfection.
Remember: this book is originally not about computer systems. Not at all. But read it anyway!
I have read several other books about Lean Startup, but this is the one I would recommend now, especially if you already have some experience with startups.
It exposes the Customer Development model, composed of four steps: Customer Discovery, Customer Validation, Customer Creation and Company Building. The most interesting ones, in my opinion, are the first two, the ones that occur before Product / Market Fit.
Read this book if you want to understand what this Lean Startup thing is about, and offer it to your friends who want to start their own business.
This book is an answer to Michael Lewis’ Flash Boys and a defence of High-Frequency Trading.
I have long thought that the financial world used HFT as a scapegoat (especially since I read Chris Stucchio’s blog). Flash Boys: Not So Fast follows the structure of Lewis’ book; it provides a nice overview of what HFT is and why it may not be as evil as you think it is.
French politician Axelle Lemaire tweeted today that, like half of French voters, she was in favor of electronic voting. This tweet has resulted in a strong reaction among most of my techno-saavy friends which could be summed up as “OMG no, e-voting cannot be secure, please educate yourself”.
On this topic, my views differ with the majority opinion among people in technology, even though I support the same organizations fighting abuses in e-voting such as Electronic Frontier Foundation.
If you speak French, please read these three articles by LORIA reasearcher Véronique Cortier. They may change your mind on e-voting somehow. In particular, she argues that, although e-voting cannot be as secure as physical voting, it could very well be better than absentee voting.
Véronique is one of the researchers working on Belenios, which is Open Source and scientifically proven e-voting software. Belenios provides privacy, verifiability and full correctness, properties which many erroneously think cannot be satisfied simultaneously.
I am not denying that there are issues that will be hard or impossible to solve. The two main ones, for me, are the security of client terminals and the capacity for random citizen to trust the process. Indeed, understanding and verifying something like Belenios requires relative proficiency in math and computer science. I am willing to let the scientific community do that work, but some consider this view elitist.
However, I would like to see more research and experiments with e-voting. I think that making the organization of votes easier and cheaper will allow our political systems to evolve towards something where citizen have more direct influence on the democratic process.
I have to disclose that I know one of the authors of Belenios (Stéphane Glondu) personally, which is how I came to learn about systems like Helios and Belenios in the first place.
Even among software people, those of us who work with distributed systems and algorithms are sometimes seen as mad scientists. We use words like like consistency, causality, consensus, commutativity, idempotence, immutability and “impossibility theorems”. How come we have to read papers and tear our hair out just to make software run correctly on a few machines? Are we all failed academics incapable of pragmatism?
I have selected four documents to help you understand the kind of things we spend our days thinking about, the physical limits we work with and the trade-offs we make. You may want to read them, because distribution is becoming the norm rather than the exception, so you may have to think about at least some of those issues sooner than you expected. Those are simple documents that any programmer should be able to read. Only the last one is an actual paper, and it is a simple position paper with no math, I promise.
Most distributed systems are asynchronous, which means that the usual notion of time does not make sense within them. This article by Justin Sheehy, former Basho CTO (now at VMWare), published in ACM Queue in March 2015, is about that fact and its consequences. It inspired me to make this list, because as soon as I read it my first thought was that I should share it with all of my colleagues and every single person I will ever work with. It introduces two important impossibility theorems (FLP and CAP) and adversarial models, discusses the existence of “reliable” clocks and networks, and touches on some solutions such as consensus protocols and CRDT.
This 2008 blog post by Werner Vogel (Amazon CTO) explains the trade-off between availability and consistency, introduces consistency models, and hints at the relationship between the replication factor of data and the level of consistency that can be achieved. If you want to go further after that, you can read the famous Dynamo paper (which is not too complicated), preferably in its version annotated by Basho.
This document, by Mikito Takada (Trifacta), is longer than the others. It is the best short (meaning: not a whole course or book) introduction to distributed systems I know. In a nutshell, it expands on the ideas presented in the first two documents and gives you most of the necessary concepts and vocabulary to understand actual distributed systems papers, should you want to read them.
I could hardly leave that 2009 paper by Pat Helland and Dave Campbell out of this list. It starts with the idea that, as the distribution of systems increases, latency makes synchronicity intractable and pushes us towards asynchronous designs. The rest of the paper is a discussion of the trade-offs involved. The very interesting idea in that paper (also found in other papers by Pat Helland) is that it all comes down to a problem of risk management and reconciliation. If asynchronous software can not ensure something will happen, it may make a guess and fix the result later if it was wrong. That may include having to make excuses to an actual human being. An important corollary is that the distributed nature of software permeates all the way to the user interface (which is why you may have seen me ranting about how we need to start forming cross-functional teams, with developers who understand UX and designers who get distributed systems).
I said on Hacker News that, if I wrote that article today (in May 2016), I would add this article to the list. I was advised to edit the original blog post, so I did.
Published in April 2016 in ACM Queue, this article by Carlos Baquero and Nuno Preguiça may be the best introduction to causality I know. Causality is a very important concept in distributed systems. In a nutshell, the idea is to answer the question: given two events, could one have influenced the other? In other words: when the entity performing the second event did, dit it have any knowledge that the first happened? This article introduces causality and gives usual ways to represent it in theory, with causal histories, and in practice, with vector clocks and version vectors as well as their dotted variants.
When I started programming as a child, I was hooked by the idea that the computer was a perfect machine that, unlike a teacher, would never be unjust: it would work if I got the code right, and if I did not I just had to fix it and try again. Once it worked, it would always work! Everything was reproducible, and all issues could be diagnosed and understood.
Of course, I grew up and the messy reality caught up with me. I went into distributed systems, which are a lot more like physics than maths. There are laws that govern the world out there, and they are always coming in your way. Everything works as expected, until something unexpected happens. And sometimes, you will not even be able to explain what it was after the fact.
Distributed systems work is about that adversarial world out there, and how we write programs to cope with it. It is about dealing with the unreliability of clocks, communications, hardware and, sometimes, people. It is about reading papers, drawing diagrams, writing proofs sometimes, and finding solutions to obtain the guarantees we want. More than anything else, distributed systems is the science (or art? or game?) of trade-offs. It is hard, error-prone and usually broken, terribly complicated sometimes, but it is my field, and so far I like it.
Clarification: The title is exaggerated. I have never hated LuaJIT, I just went back to using PUC Lua primarily.
I started using Lua in early 2007. I had already been programming for years, but I had reached a point where I had decided that I wanted to know exactly what was going on when my computer executed dynamic code. That meant understanding two critical pieces of the software stack: the OS and the interpreter.
I already had notions of how an interpreter works and I could tell the main functions of a Unix kernel, but really understanding software, for me, implied reading its source code. I quickly realized that I would not succeed if I started with Perl or Python and Linux, which were what I was using at the time, so I set my sights on Lua and Minix3.
Knowing software also implies using it, so Minix3 and Lua became my main OS and language for several months. To be honest, I was running Minix3 in a VM due to the lack of drivers for my hardware. I started to study their code, with the help of the book for Minix3 and resources found online for Lua. I learnt a lot during that period.
Eventually, I went back to Linux for practical reasons, but I had been charmed by the down-to-Earth elegance of Lua. It became my favorite programming language. I was still a student though, so I mostly wrote small utilities, Web pages and game prototypes with it. Coursework was Java, Python and C, and for “serious” stuff at ResEl I used Perl, Python or Bash.
Then in 2010 I went to Moodstocks for my MSc Thesis. We were a very tiny startup then, just the founders and a couple of interns. They were a Ruby and C++ shop, so I learnt Ruby, but I didn’t like it: too much magic, and the object-oriented interface to write bindings was a mess compared to the very clean Lua C API.
Eventually, Moodstocks hired me and I worked for them for over three years. I ended up writing most of their server-side code, I already gave details here. A lot of that code leveraged LuaJIT. It was the perfect tool for the job: as flexible and dynamic as Ruby so it worked well as a scripting language, about as fast as C++ so it could be used to implement Computer Vision algorithms, and the FFI made it very easy to call into our existing C / C++ libraries.
Those are the features that make LuaJIT so attractive: its blazing speed and the FFI. They are why I could write Lua professionally for several years, and I could never thank Mike Pall enough for this. Moreover, LuaJIT has been playing a huge role in the growing popularity of Lua those last few years.
However, paradoxically, LuaJIT negates the very reason that made me pick Lua in the first place. And by that I mean: I do not deeply understand how LuaJIT works. I have tried, and I will certainly try again, but it is one or two orders of magnitude more complicated than PUC Lua.
Beyond the purist ideal of understanding the whole stack, this has practical implications. When you find a bug in LuaJIT, understanding it and fixing it is terribly complicated. The best I can do is usually to try to produce a small test case that reproduces the bug (even that is not always easy) and hope Mike Pall finds a fix. With PUC Lua I could probably fix it myself - but of course PUC Lua is so simple that I have never found a bug in it!
Another issue is that PUC Lua and LuaJIT are diverging. LuaJIT implements Lua 5.1. Lua 5.2 code can be made to work as long as it does not use _ENV, but code that leverages the new features in Lua 5.3 is not supported at all (although modules like lua-compat-5.3 can probably help). On the other hand, more and more modules require the FFI, making them incompatible with PUC Lua (of course I am a culprit of that myself). And to top it all, the interpreters work so differently that efficient code in LuaJIT is not necessarily good in PUC Lua, and vice versa.
These days, the purist, simplicity-loving part of me tends to win over the pragmatist in my (rare) non-work code, and my current target of choice is PUC Lua. At work, I write almost exclusively C and almost no Lua, and the little I do write runs on a machine which does not support LuaJIT. The only reasons I still use LuaJIT are maintenance of luajit-msgpack-pure and my use of OpenResty, which for my purposes could as well be built with PUC Lua instead.
That being said, I like knowing that I could take my Lua code and, with a few tweaks and a change of interpreter, get a serious performance boost. So I still hope someone (if not Mike Pall) will make a version of LuaJIT that supports the Lua 5.3 interface. Otherwise, I guess I can still decide that learning more about JIT compilation is worth it and spend a few months diving into that LuaJIT codebase!
Oh, and by the way, with this whole systemd thing, I am semi-seriously considering giving Minix3 a second chance :)
Travis is a Continuous Integration service which is free for Open Source projects and has very good GitHub integration. We will see how to use it for your Lua projects.
Your test suite will work well with Travis as long as executing it returns 0 on success and nonzero on failure. If you use plain Lua assertions, it is already the case. If you use a test framework, make sure that it works that way. I have added a helper to cwtest for that purpose.
Travis does not support Lua out of the box, but using it with Lua projects is not hard because moteus has done all the hard work for you. You just have to clone this repository and copy the .travis directory to yours.
After that, you only have to write a single YAML file, .travis.yml. For example, here is the one I wrote for Haricot.
Most sections should be self-explanatory. install is where you set up your dependencies. The first line calls moteus’ script which lets you use Lua and LuaRocks. A separate build and test run will occur for every Lua version declared in matrix; you can comment some lines there if you do not want to test some Lua versions. For Haricot I need Beanstalk running in the background so I start it in before_script. script is where you run your actual tests.
To enable Travis, sign up, allow Travis to access your GitHub account, then go to your profile and flip the switch for your repository:

After that, commit .travis and .travis.yml and push to GitHub. It will trigger a test build, and so will every subsequent commit. If you want, you can now add a badge with the status of your build to your README or your project’s home page.
It is not a secret that I don’t like Web technology. I prefer XHTML to HTML5, I think JavaScript is a terrible language, and don’t get me started on microformats (let’s just say that SoC > DRY…). I’d rather see this mess replaced by something much simpler that only deals with linked documents and feeds, and maybe a separate platform for portable applications.
However, the Web is here to last, and given its importance, its centralization is concerning. This is why I am interested in the Indie Web movement, which I see as part of the larger effort to decentralize Internet. By the way, if you are into this and live in Paris, check out this meetup which is held every six weeks at Mozilla’s office. I have been there every time since its inception and it is well worth it.
Anyway, I have seen today via my feed reader that some people had made commitments for 2015-01-01. I am a bit late, but I decided to follow suit by adding Web sign-in and a h-card to my home page. I will probably add h-entry markup to this blog later this week, too.
If you want to do the same, you can use this handy tool to check everything is working.
Two people I respect a lot, Avdi Grimm and Michel Martens, are having an interesting debate about the complexity of programming tools and libraries.
In the Ruby community, Michel is well-known for writing simple tools that do their job well. Avdi defends the framework approach of Rails, arguing that using fatter tools allows you to make your own code simpler. If you want to hear them debate it, listen to the podcast.
It will not surprise people who know me that I side with Michel here. Actually, I am probably more extreme than he is: I used his Redis Object Mapper at Moodstocks some years ago, but I eventually went back to using redis-rb directly, and finally dropped the Ruby language entirely. You can also see by yourself how much I obsess about simplicity by looking as my list of quotes.
I feel like most programmers do not reason like Michel and me regarding this, and it seems to me those who do often have similar backgrounds in Unix and maintenance of production systems. Maybe as a result, we tend to take a system approach to everything, so when we evaluate the complexity of software, we take into account the complexity of dependencies as well as the complexity of the application code itself.
When running production systems, the most important thing you want to optimize for is the speed with which you can diagnose and recover from a problem. Reliability is also important, sure, but you quickly learn that no matter how good the software you use is, it will fail (coincidentally, another host of the podcast talks about that on her blog). For that purpose, you want few moving pieces, each one being simple enough for you to understand it.
You can take this idea very far. Around 2007 I thought about how nice it would be to know a programming language so well that 1) there would be nothing in it I would not know about and 2) I would be able to know exactly what every line of code I wrote did internally. At the time, I wondered if it would require me to pick a language like Forth or LISP implement it myself. It turned out not to: I discovered Lua, whose reference implementation is roughly 15000 lines of (readable) C code. It has been my dynamic language of choice ever since.
When I have a problem to solve, I look for the simplest Open Source tool that does it and weigh the cost of implementing the feature myself against the cost of using and maintaining this tool. Tools usually win when they just do what I wanted; they lose when they do too many things or pull in too many dependencies I did not already use.
Many Ruby programmers, when they install a gem that pulls in several dependencies and compiles some C code, think: “How nice! All of this is automated for me!” The reaction of an operations person, on the other hand, is closer to this:

When I write Open Source tools myself, I try to reason the same way. For instance, I wrote a small Beanstalk client for Lua called haricot. The protocol used by Beanstalk has a few methods that return YAML. Those methods are for monitoring and are typically not used by job producers or consumers.
YAML being a terribly complicated format (please do not use it), all the YAML parsers I know about in Lua land are bindings to C libraries, making them annoying to install and maintain. I had to decide between choosing one of them and making it a dependency or writing my own code to parse the subset of YAML used by Beanstalk.
I chose a third solution: returning raw YAML to the user. Yes, this is “pushing the complexity upstream”. But most users of this library will never need those methods. In the test suite, I auto-detect the presence of a YAML parser and skip related tests if none is available.
Eventually, this is a matter of choice. Avdi is right: fat tools usually exist for good reasons, not because their authors did not think of a simpler solution. Choosing whether to use them or not has to be a conscious trade-off. I just personally decided there are very few things I want to trade simplicity off against.
These days Linux systems tend to open graphical password prompts when a CLI application needs user authentication. I don’t know about you but I really don’t like that.
The first offender is git, which uses x11-ssh-askpass if installed. The simplest solution would be not to install it but it is a dependency of virt-manager on Arch Linux… Thankfully you can tell git not to use it:
git config --global core.askpass ""
The second one, in my case, was pass. If you try to use it in Gnome, the keyring hijacks the GPG agent and you get that message (plus a graphical prompt):
gpg: WARNING: The GNOME keyring manager hijacked the GnuPG agent.
gpg: WARNING: GnuPG will not work properly - please configure
that tool to not interfere with the GnuPG system!
The Gnome keyring is an annoying piece of software that replaces password prompts for several tools including SSH and GPG. you can disable it this way:
mkdir -p ~/.config/autostart
cd !$
cp /etc/xdg/autostart/gnome-keyring-* .
for i in *; do echo "Hidden=true" >> $i; done
… but then GPG will use yet another graphical prompt! To finally stay in your
terminal, create the file ~/.gnupg/gpg-agent.conf with the following content:
pinentry-program /usr/bin/pinentry-curses
Several people have asked me what I think about microservices. The tl;dr is: I like small services, but I don’t like what some call microservices, which is isolating every single feature within its own service and aiming at services at small as possible (I heard about a target of “a few hundred lines of code” per service and a hard limit at 5000 LOC).
I see SOA (and modularization in general) as a technique to design a system so that it scales with the number of people on a team. The way it works is by dividing complexity between people. The following will be terribly simplified, but bear with me.
Imagine a team working on a monolithic application. It is becoming too large and complicated to understand, so they split it into three parts, A, B and C. A third of the team will be responsible for each part. Each team is tasked with exposing an interface to the others, so that team A only has to worry about the internals of A and the interfaces of B and C. For each team, complexity has gone from the complexity of the monolithic application to a third of that complexity plus the complexity of communicating with the other parts of the system, or at least just knowing they exist, so given N the number of parts in the system and M the total complexity of the system, complexity seen from a service is: M/N + k*N.
Now assuming the complexity of the system increases linearly with team size S (it probably increases faster in practice but let’s approximate), complexity seen from a service is l*S/N + k*N. With everything else constant, the function N(S) to minimize that looks like a square root.
In practice, this is not entirely true: as the system grows, not every service talks to other services and not every developer needs to know about every service. But because system architects and, more importantly, operations people do, my argument still holds.
So here is my point: the number of services you have should look like a constant times the square root of the size of your team. Meaning, with a constant of three:

This is the problem I have with the idea to bound the size of single services while ignoring the complexity of inter-service communication. SOA is a practice which can help you curb local complexity as you scale but there is no way it can make it constant without making a mess of the whole system.
That being said, the value of the constant can be discussed. Some people think it should be lower than one, others think it should be very large.
I am not a huge fan of small constants when they result in services that do too many things and require too many people. They end up looking like a few monoliths, with the same issues as a single monolith. Moreover, if you are going to do services, inter-service calls should be the norm and not an exception. Very large constants, on the other hand, result in too much accidental complexity, harder debugging and operational nightmares.
I guess mileages vary but I like numbers around three. If you look at the curve above you may (or may not) agree that it looks reasonable; I think it does.
Note that the title of this blog is still “Separate Concerns”: you should still draw clear lines between services, and you should still modularize as much as possible within services. But not every function call needs to be turned into a message sent over a network, and not every data structure needs its own process and source control repository.
And finally, just to be clear: do not look too much at the left part of the curve if you are a very early stage startup looking for product-market fit. You can still - and probably should - start with a monolith as long as you choose an architecture or framework with good modularization capabilities (like Flask Blueprints). Only consider SOA when you start to have a good rough idea of what the product will look like.
Iris is a “decentralized Cloud messaging” middleware that I have really started looking into with the recent release of version 0.3.0. It had struck me as interesting when I first heard of it at FOSDEM 2014. The reason for that, beyond the great presentation skills of its author, is that it implements principles I think are sound to build SOA upon.
In Iris, the logical and physical layers of services are cleanly separated. Each box in the system runs a local instance of the Iris broker (“Iris node”). The broker is written in Go, and all broker instances in the system converse in a proprietary protocol.
To provide or consume a service, you never have to open a connection to a remote machine: you just talk to your local broker using the Iris relay protocol. This means you can use any programming language you want as long as you implement this protocol, which is much simpler than the protocol used between brokers. There are official clients for the relay protocol (Iris calls them “bindings”, but I think that name is confusing) in Go, Erlang and Java. I have written one in Lua that I will use for examples in the rest of this post (if you prefer, you will find corresponding code in Go for most examples here).
In Iris, services are represented by names. To provide a service you register with its name, to consume one you address it by its name. That means you never dial into a specific instance of a service explicitly. From the consumer’s point of view, the service could be provided by a single box as well as hundreds across different datacenters. Boxes can go up or down, and you will almost never have to care about it when writing code: it is an operational concern. The way Iris’ author puts it is that in Iris the smallest logical entity is a cluster, there is no concept of individual machine or process.
Iris supports three usual basic patterns for communication: Request/Response, Publish/Subscribe and Broadcast. A fourth one, Tunnel, is slightly more complicated.
This is the pattern everybody knows about from HTTP, and probably the workhorse of most SOAs. A client sends a request to a cluster, and a single node of the cluster answers it. Iris just does the messaging here: the request and the response are binary blobs, there is no notion of headers.
Here is how it looks in Lua on the client side:
local iris = require "iris"
local c = iris.new()
assert(c:handshake(""))
local req = c:request("echo", "hello", 1000)
c:process_one()
local r = assert(req:response())
c:teardown()
print("reply arrived: " .. r)
and on the service side:
local iris = require "iris"
local c = iris.new()
assert(c:handshake("echo"))
c.handlers.request = function(req)
print("request arrived: " .. req)
return req
end
for i=1,5 do c:process_one() end
c:teardown()
Another well-known pattern. Consumers subscribe to a channel identified by a name, producers send messages into the channel and all subscribers receive them. There is no response from the subscriber. Channels are completely independent from clusters, i.e. channel “X” and cluster “X” can coexist and have nothing in common, and there is no restriction that a channel only works within a given cluster (any entity on the same Iris network can access it).
Here is a publisher to channel “somechan” in Lua:
local iris = require "iris"
local socket = require "socket"
local c = iris.new()
assert(c:handshake(""))
for i=1,5 do
c:publish("somechan", "message " .. i)
socket.sleep(1)
end
c:teardown()
and a consumer:
local iris = require "iris"
local socket = require "socket"
local c = iris.new()
assert(c:handshake(""))
c.handlers.pubsub.somechan = function(msg)
print("message arrived: " .. msg)
end
c:subscribe("somechan")
for i=1,5 do c:process_one() end
c:teardown()
We have seen that requests are sent to a single instance of a cluster. What if you want to notify all members of a cluster of some event? This is what Broadcast is for. It is kind of redundant with Publish/Subscribe (you could have all members of each cluster subscribe to a channel) but it is somehow cleaner to have a separate message type for this. You could use it to build more complicated things on top, for instance a full-fledged service bus.
Here is how to send a broadcast message in Lua:
local iris = require "iris"
local c = iris.new()
assert(c:handshake(""))
c:broadcast("bcst", "hello")
c:teardown()
and how to handle them:
local iris = require "iris"
local c = iris.new()
assert(c:handshake("bcst"))
c.handlers.broadcast = function(msg)
print("message arrived: " .. msg)
end
for i=1,5 do c:process_one() end
c:teardown()
Tunnels are the last and most complicated way to communicate provided by Iris. To understand why we need them, remember how requests work.
The fact that any member of a cluster can answer a request prevents you from doing anything stateful, because you cannot know if you are talking to the same instance or not in two separate requests. Moreover, the nature of the request and response does not allow any of them to be composed of multiple parts sent in separate messages. A request cannot trigger multiple responses.
Tunnels are a solution to all that. When you open a tunnel, you address a cluster, but what you obtain is a persistent, bidirectional, ordered pipe to a specific instance of that cluster. You can think of it as a TCP socket or a circuit if you come from a telecom background (although there are no latency guarantees). Tunnels implement a throttling algorithm so that both ends of the tunnel can specify a maximum size for their input buffer.
For now, here is what I think of tunnels in the context of a SOA: they are very powerful but tricky to use. If you can avoid them, you should. If not, you should not use raw tunnels anyway: you should define your protocol, write an abstraction for it and never expose the tunnel itself to the application.
One valid use case I can see for tunnels, and which is used in Iris examples, is streaming. But this can go much further. Like the name indicates, you could tunnel most protocols into them, so they could serve as the foundation for some kind of VPN.
Anyway, here is an example client with multiple responses and state:
local iris = require "iris"
local c = iris.new()
assert(c:handshake(""))
local tun = c:tunnel("tunnel", 1000)
tun:confirm()
tun:allow(1024)
tun.handlers.message = function(msg)
if msg == "data" then
print("got data")
elseif msg == "continue" then
print("got continue, sending request")
tun:cosend("request")
elseif msg == "bye" then
print("got bye, quitting")
tun:close()
else
print("got invalid message: " .. msg)
tun:close()
end
end
local xfer = tun:transfer("request")
while not xfer:run() do c:process_one() end
local op
while op ~= iris.OP.TUN_CLOSE do op = c:process_one() end
c:teardown()
and the server on the other end:
local iris = require "iris"
local c = iris.new()
assert(c:handshake("tunnel"))
local MAX_COUNT = 3
local count = 0
c.handlers.tunnel = function(tun)
tun:allow(1024)
tun.handlers.message = function(msg)
if msg == "request" then
if count < MAX_COUNT then
count = count + 1
print(string.format(
"got request, sending %dx data + continue",
count
))
for i=1,count do tun:cosend("data") end
tun:cosend("continue")
else
print("got request, sending bye")
tun:cosend("bye")
end
else
print("got invalid message: " .. msg)
tun:close()
end
end
end
local op
while op ~= iris.OP.TUN_CLOSE do op = c:process_one() end
c:teardown()
Iris is a very interesting piece of software which implements a vision of systems that I like and cannot find in any other product out of the box. It is still very young and probably not ready for serious production yet, although I have found it stable during the (limited) testing I have done. Even though my current job does not involve SOA I will probably come back to it someday, so Iris will join the list of tools whose progress I follow attentively.
On a side note, Iris was built for its authors’ thesis, and he is now looking for a sponsor to allow him to continue working on it. If you have the power to make that happen, give it a look. Think how VMWare / Pivotal must be happy of the deal they did with Antirez :)
Finally, if you try my Iris client and find bugs, do not hesitate to report them on Github! I have no real world Iris system to try it on so there are certainly plenty.
Note: I posted what follows as a Gist over a year ago. Recently I was looking for it and couldn’t find it, so I am re-posting it here for the next time.
At the last Human Talks Paris meetup, Fabien Charbit (founder of sush.io) gave a talk in French whose title could be translated as: “Programmers, create your company!”
Of course I agree with the idea, but at the end of it I said I was disappointed because he said you should find a marketing / commercial-minded CEO and be CTO. This is a commonplace, and I thought his message would be bolder than that. You can always discuss whether this is right or wrong but I think a tech startup where all the founders are tech-minded makes a lot of sense.
It turns out, Paul Graham said exactly that in a video interview back in 2005. He is is much more credible than me on the topic, so I reproduced what he has to say about it below.
Note that Fabien later clarified that this was not an issue of degree or even competence, but rather that one of the founders should have the motivation to deal with the softer aspects of the company, so we agree after all. But it is still way too common in France to assume every startup’s CEO should have a business degree…
Anyway, the question was: “What is the relationship in startups between programmers and the business types?”, and PG answered:
The relationship between the programmers and the business types? Well… I believe, and Y Combinator is kind of an experiment to test this… I believe that programmers can become business types. I think that business is kind of like chess, in the sense that the hard part is not knowing the rules about how the pieces move, the hard part is actually being able to, like… look ahead and make strategies and stuff like that, right. The hard part about playing chess well is being smart, right, not knowing how to play the rules of chess. And I think business is like that, that there’s a few rules of business and that hackers are capable of learning them, most hackers, and that… once they learn them, they’ll be as good at it as business guys, you know? So… I think hackers can be business guys. Hackers are perfectly capable of being business guys. Look at Bill Gates, right! He didn’t go to business school. I mean… you might wonder, why would anyone want to go to business school? You know, when you look at the example of Bill Gates, he seems to be doing fine at business, right? He sort of picked it up as he went along, and that did not seem to hurt him at all. Larry and Serguey, they didn’t go to business school either, right, they’re just hackers, and they seem to be doing fine too. So I think the relationship between hackers and business guys, at least in the beginning, is that you need hackers and you don’t need business guys.
I have just watched a small TEDx talk by Simon Peyton Jones (of Haskell fame) on CS education. Something he said struck me as relevant to what I was saying in my last post:
Arthur C. Clarke once famously remarked that “any sufficiently advanced technology is indistinguishable from magic”. And I think it is very damaging if our children come to believe that the computer systems they use are essentially magic. That is: not under their control.
If you have a programming background, you may experience a feeling close to disgust when you hear the word “magic”. But I have heard “product people” use it as if it was a good thing way too many times. Short term, maybe. But eventually I want to master my tools, to understand them inside out. I want to be able to rely on them, and for that I expect them not to surprise me.
I think it is time for the pendulum to swing back to products that optimize for how high the asymptote of the learning curve is, not the time it takes to reach it. The kind of products that come with manuals and teach you things instead of trying very hard not to make you feel stupid, or in other words: not to make you think.
I have long been thinking that there is something wrong with modern product design thinking. I see designs that trade off almost all power, flexibility and composability for a smoother learning curve. I see designs that remove explicit controls and replace them by magic, choosing to hide essential complexity instead of reducing accidental complexity. I see designs optimized for new users and prospects instead of regular users, and designers who apparently consider documentation as something evil.
I do not like that trend. Learning from a community where RTFM was often the right answer has been very beneficial to me when I was a child. I am also a proponent of simplicity and complexity without complication, as should be obvious from the quotes I have been collecting over the last years.
The name of Alan Kay can be found a few times in this file, and so I was very pleased to find out that he recently gave a talk where he puts words on that hard-to-describe feeling: that this design school, which postulates that educating users is a bad idea and that everything should be natural, is hindering progress.
I found it so spot on that I decided to transcribe part of it below. I encourage you to read, and then watch the whole video if you can!
Human beings tend to hate learning curves, […] and marketing people really hate learning curves. […]
Any product today that requires a substantial learning curve is not what marketing people are looking for. As the joke goes, they want a brand new idea that has been perfectly tested. They would like something that people instantly recognize, but you’re the only person who has it. So they want something, essentially, that would have appealed to any cave person 100000 years ago, something that fits into what our genes set us up to be interested in.
An interesting question is: if the bicycle were invented tomorrow, would it actually be carried through? Think of how dangerous a bicycle is, think of the lawsuits! The bicycle is only tolerated today because it has been around for a long time, when people didn’t sue when a kid got danked.
Another way to look at it is that the larger world out there is kind of a low pass filter. And this is still going on: the iPad has a much more brain-dead interface than the Mac. […] If you think of the iPad as a gesture device, a gesture is inherently something that gives you not just naturalness but efficiency. And so when you’re doing gestures-oriented computing, and the origin of that goes back into the 60s - there were some really wonderful systems back then, what you really want to do is to learn a bunch of gestures to make you fluent and efficient on the thing. And the iPad doesn’t have any particular way of teaching you those gestures. They don’t force the developers to put a teaching thing for those gestures in there. And so basically everything is devolved down to the few simple gestures that are generic to the iPad.
This is kind of a dumb-down-ism that has been incredibly successful for people who are only interested in making money. It has not been good for personal computing. […]
If we look at human psychometrics, […] when a new idea or tool appears, about 95% of us are what are called instrumental reasoners. […] And an instrumental reasoner is a person who judges the new idea or tool on whether it will advance their current goals. […] They are very conservative about shifting their goals. 5% of us are interested in the new idea or tool just because we’re interested in new ideas and tools, and many of these people actually change their goals when a new idea or tool appears.
If we look at the other axis, about 85% of us do things primarily for social approval. That’s what extroversion actually means: it doesn’t mean you’re a performer, it means you are actually interested in the opinions of others. About 15% of us are inner-directed.

If you combine these two (and I realize that they might not be completely independent dimensions, but they are independent enough for this talk) you get this interesting thing: 1% of us is inner-directed, not so interested in the approval of others, and intrinsically interested in new ideas and tools.
And 80% of us are goal-conservative, instrumental and directed by what our society thinks of us. This group requires almost everybody to agree on something before anybody agrees on something. […] So this group generally cannot do something just because it is a good idea. This is just not a concept that this group has, the majority of human beings. They’ll do something if it is actually part of a sanction. And so this group is capable of doing things that are terrible ideas […] if they’re sanctioned by the group. […]
And it turns out of you want to make a change in the larger world, you have to do something with the 80%. The 1% are more or less always with us and doing things. Some eras they get burnt at the stake, some eras they get rejected. In the 60s they actually got funded for a while. Xerox PARC came out of the funding of those people in the 60s, but they’re always with us.
I like to learn new tools and concepts by experimenting with small projects whose sole purpose is to help me grasp something better. Working on a larger project in a team with other people is invaluable, but doing things on a smaller scale on my own offers a different perspective.
Having recently started to write a lot more C at work than I used to, I felt that it was a good idea to do a smaller project in the language. It turned out Build Your Own Lisp came out just when I needed it, so I had a go at it.
I cannot recommend this book enough if you feel like you would enjoy building a dynamic language in C from scratch (using just a parser library). It is one of the best tutorial-style books I have ever read. It achieves a perfect balance between being didactic and leaving freedom to the user.
My Lisp ended up being different from the one described in the book in several minor ways. For instance: it has more types, builtins take expressions instead of values, values are based on a union (which saves memory), memory management works differently, the source more organized… In that respect the book delivers exactly what it promised: the reader is encouraged to build “his own” Lisp.
In the end I got just what I wanted from this experience: I clearly improved my C and had the satisfaction to write a working programming language in a few hours scattered over three weekends.
Oh, just in case you were wondering, nobody paid me to write that blog post!
Next, I will probably be learning Go. Go is a weird language in that about half of the technical people I follow and look up to like it a lot, and the other half hates it. The latter tend to be users of languages with stricter type systems…
I have to admit that, if I had looked solely at the technical merits of both languages, I would probably have learned more Rust instead. I already know some Rust, but not enough to use it productively. Its main advantages are its (arguably) better type system and the fact that it can run without a GC or a frontend, making it suitable to write dynamic libraries.
However, Rust doesn’t look completely stable yet, whereas Go is already used in production by several serious companies. Also, I am a distributed systems programmer, and most of the interesting codebases I see popping up around in that field are written in Go.
Moreover, Go appears to be a simpler language than Rust. I suspect I can learn enough of it to read code “fluently” and write some much faster. So Go it is, and Rust will probably be next.
I have finally decided to configure Sublime Text 2 to have it autocomplete Lua code the way I want it to.
For instance, by default, when you start typing function and hit TAB,
you get the following result:
function function_name( ... )
-- body
end
Instead I wanted this:
function(self)
error("unimplemented")
end
It turns out this is very simple. Go to Preferences -> Browse Packages...
and open the Lua directory. There are several files there, including two
named function-(fun).sublime-snippet and function-(function).sublime-snippet
which do almost the same thing.
Remove the first one and open the second one in a text editor. Its content should be something like:
<snippet>
<content><![CDATA[function ${1:function_name}( ${2:...} )
${0:-- body}
end]]></content>
<tabTrigger>function</tabTrigger>
<scope>source.lua</scope>
<description>function</description>
</snippet>
Replace it by:
<snippet>
<content><![CDATA[function(${1:self})
${0:error("unimplemented")}
end]]></content>
<tabTrigger>function</tabTrigger>
<scope>source.lua</scope>
<description>function</description>
</snippet>
… and that is all, the deed is done!
Last weekend I attended FOSDEM, the largest Open Source conference in Europe that takes place every year in Brussels. This was my fourth year in a row. Not much Lua this year, although I saw some familiar faces. But I did listen to lots of interesting talks which I will try to summarize briefly.
Debian developers want to provide their users a way to verify that the binary packages they distribute correspond to the source. To achieve reproducible builds they have to patch code that depends on things such as timestamps at build time. To make things worse, using a standard VM for builds would help but they refuse to do it “because they’re Debian.”
The Gentoo leader thinks distribution are doing a poor job of meeting the needs of users - especially developers and system administrators - in terms of package management. They are increasingly relying on configuration management tools (CFEngine, Puppet, Chef, Ansible…), language-specific package managers (RubyGems, NPM, LuaRocks…) and tools like Docker; however, distribution package managers do not integrate well with those.
This is a topic that interests me, following all the discussion that occurred at the last Lua Workshop. After the talk I asked Donnie whether there was a discussion list somewhere dedicated to those issues. He told me that, to his knowledge, there was not. It may be a good idea to start one.
Kees talked about the Minix 3 architecture, how it was ported to ARM (with the BeagleBoard and BeagleBone devices as first targets) and plans for the future.
The main priority of the project is porting the NetBSD userland. As a former user I think this is an excellent idea. I may try the OS again someday if it becomes more “usable”, I really like some of the ideas behind it.
Sailfish is probably the most interesting mobile OS project today, go check it out if you don’t know it yet. Carsten gave us an overview of the main parts of the OS and explained some funny things they did, such as making glibc and the Bionic libc co-exist within the same process. He also provided the audience an SSH access to an actual terminal. I found liblua 5.1 on it :)
This talk introduced the Postfix least-privilege architecture, and then went on to explain recent improvements. Most of them revolve around fighting spam more efficiently. Postfix also migrated from Berkley DB to LMDB (see later), mostly for licensing reasons.
Nix OS is a Linux distribution built around the Nix package manager. It is one of those few distributions that completely disrupt the FHS, another one being GoboLinux.
The idea of Nix is that a package is the output of a function provided with some arguments and without side effects. There are a lot of interesting ideas there, such as atomic updates based on symlinks. However, doing this involves some heavy patching and that makes me uncomfortable.
Iris is a messaging backend written in Go but with a language-agnostic interface. It is somehow similar 0MQ but goes further by removing the notion of a specific instance or process in favor of sets of those. Each machine runs a single daemon that does all the heavy lifting, and each client connects to it locally to interact with the system.
This is a very interesting project and I was a bit surprised I had never heard about it until now. The slides and live demos were also really good, with multiple Go code snippets running concurrently in a browser. Oh, and Gopher drawings!
The speakers showed off what the latest release of Camlistore, a personal file storage system, can do. I already knew and liked the technical design of the project, but I was impressed by the progress made on usability since Brad’s talk at dotScale 2013. They have a new Web UI which is not complete yet but looks very promising. I really have to install my own node someday.
A very interesting talk on LLVM and how it helps when statically compiling a (very) dynamic language such as Ruby. Among other things, the optimization passes do an incredible job. However, it looks like JIT compilation is not as good as static compilation yet.
This was, as I had hoped, mostly a talk about LMDB. It confirmed what I already suspected: at least according to its author’s benchmarks, it outperforms any kind of competition in this space (embedded key-value stores). It is also one of the rare NoSQL DBs to implement MVCC transactions, in less than 10000 lines of C code and 32 kB of object code. If there is a piece of Open Source software I think you should not have missed in 2013, it is this one.
An interesting talk about how the shift away from rotating disk towards persistent memory storage affects the way we develop filesystems, kernels and any code that does I/O. This is not only about SSDs, but also about new kinds of devices that will come out soon and be an order of magnitude faster according to Ric.
Benoit explained how he ported the Go concurrency model (Goroutines and Channels) to Python. I know Benoit and his offset project so this was not foreign to me, but there are two pieces of interesting news: first, the next version will support multiple processes, freeing users from the GIL (yay!), and second he is thinking about changing the API to make it more like Julia and less like Go, because Julia is more similar to Python.
This was a fun talk where phk endorsed the role of a NSA agent who mistakes the FOSDEM amphitheater for the European Commission and explains how they sabotage attempts to give the general public more privacy. Lots of conspiracy theory in there obviously, but given the recent events, is this really so absurd? phk argued at the end of the talk that the solution should be political and not technical.
The format of the talk was a very good idea that worked really well on my brain tired by two days of conference, but also by two consecutive nights out filled with Belgian beer and other strong drinks. :)
FOSDEM is still a very good event which you should consider attending next year. Besides the talks, you will enjoy the parties and meeting people you only knew online. I hopefully will see you there.
Hisham just published an article about his personal guidelines for writing Lua modules. Interestingly, I do a lot of things differently. Let us see how.
Policy Bit #1: always require a module into a local named after the last component of the module’s full name.
I tend to do that, but not always. Exceptions include:
path, utils or types from Penlight, where I add an x at the end of the name (e.g. pathx);
cjson which I call json;
multipart-post which I call mp (it is not a valid identifier anyway, I would have to replace the dash by an underscore).
Policy Bit #2: start a module by declaring its table using the same all-lowercase local name that will be used to require it.
Policy Bit #3: Use local function to declare local functions only: that is, functions that won’t be accessible from outside the module.
Policy Bit #4: public functions are declared in the module table, with dot syntax.
I do something entirely different. First, I never use the function sugar in Lua, so instead of writing local function f() I write local f = function().
I also do not declare the module table at the top of the module, I return it at the end, listing public functions explicitly. That means public functions are declared as locals. Arguably, they could be confused with private functions but that doesn’t bother me: if you are editing the code of the module, you probably know its interface. My public functions are also usually located at the end of the module.
To illustrate, Hisham’s module example is:
local bar = {}
local function happy_greet(greeting)
print(greeting .. "!!!! :-D")
end
function bar.say(greeting)
happy_greet(greeting)
end
return bar
I would write instead:
local happy_greet = function(greeting)
print(greeting .. "!!!! :-D")
end
local say = function(greeting)
happy_greet(greeting)
end
return {
say = say,
}
One advantage of doing this is that when you call a public function in the module itself it is a local. That means that you avoid a table lookup, but also that it acts as a private function from the point of view of other functions in your module.
To understand what I mean, imagine that we change our mind and now want to expose happy_greet as well.
Hisham’s module becomes:
local bar = {}
function bar.happy_greet(greeting)
print(greeting .. "!!!! :-D")
end
function bar.say(greeting)
bar.happy_greet(greeting)
end
return bar
Mine becomes:
local happy_greet = function(greeting)
print(greeting .. "!!!! :-D")
end
local say = function(greeting)
happy_greet(greeting)
end
return {
say = say,
happy_greet = happy_greet,
}
The first thing we can notice is that we had to modify say in Hisham’s module and not in mine. But now imagine a “malicious” user does this:
local bar = require "bar"
local fishy_greet = function(greeting)
print(greeting .. " ><>")
end
bar.happy_greet = fishy_greet
bar.say("yay")
With my module, the output would be yay!!!! :-D. With Hisham’s, the output would be yay ><>: the user is allowed to monkey patch their module in a way that has an effect on functions they do not explicitly touch.
Policy Bit #5: construct a table for your class and name it LikeThis so we know your table is a class.
Policy Bit #6: functions that are supposed to be used as object methods should be clearly marked as such, and the colon syntax is a great way to do it.
Again, not how I do it :) Using CamelCase is a good idea but in the wild I see it more often for the actual module table, not for what Hisham calls the “class” table that is associated to __index in the metatable (I call it “methods”). Usually, it means (to me) that the constructor is MyClass(), whereas with a lowercase module name it would be myclass.new(). I use the latter and Penlight is an example of the former.
Just like I do not use the function sugar, I do not use the colon syntax to define functions. Moreover, I often call methods with explicit self in the module itself. Any idea why? Yeah, same as above, resistance to monkey patches. I agree that this is not a very convincing argument given that I often skip the little local print = print dance.
Other advantages include, again, less call overhead and the ability to call methods consistently on objects before they have their metatable. This is sometimes useful in constructors.
So where Hisham’s class example is:
local myclass = {}
local MyClass = {}
function MyClass:some_method()
-- code
end
function MyClass:another_one()
self:some_method()
-- more code
end
function myclass.new()
local self = {}
setmetatable(self, { __index = MyClass })
return self
end
return myclass
I would write:
local some_method = function(self)
-- code
end
local another_one = function(self)
some_method(self)
-- more code
end
local methods = {
some_method = some_method,
another_one = another_one,
}
local new = function()
return setmetatable({}, {__index = methods})
end
return {new = new}
Of course sometimes I do not want to resist monkey patches, and in that case I use colon syntax for calls, but never for definitions.
Policy Bit #7: do not set any globals in your module and always return a table in the end.
This one I cannot disagree with. It is the only rule that has an obvious externally visible effect. Do this or you will annoy all your users.
I think this is what matters the most in the end: modules always return tables and never create globals. The rest is mostly implementation details!
Thanks to my new job I will have the opportunity to write a lot more C than in the last three years. To prepare for this, I decided to read some old C89 books again and see what I remembered. Here are some of the quirks I had forgotten (or never known about).
Declarations cannot be interleaved with other statements in ANSI C. They have to happen at the beginning of a bloc.
The default type of functions is int, i.e. f(void) {}; is valid and equivalent to int f(void) {};.
The C89 standards allows implementations to only consider the first 31 characters of identifiers. It is possible to use longer identifiers but they must differ in their first 31 characters to avoid collisions. External identifiers (seen by the linker) are even worse: the implementation can be case-insensitive and only take the first 6 (!) characters into account.
Using the wrong type on an union is usually undefined. However, if some members of the union start with the same attributes, that common initial part can be used interchangeably.
This:
const struct stuff_s {
/* stuff */
} stuff_t;
means the same thing as:
struct stuff_s {
/* stuff */
} const stuff_t;
but if you wrote it you probably meant this instead:
struct stuff_s {
/* stuff */
};
typedef const struct stuff_s stuff_t;
I already knew sequence points can be tricky, but this bit of code tricked me anyway: a[i] = i++;. There is no sequence point so the result is undefined.
The standard allows the representation of NULL to be different from 0, but its value has to be 0 so you can almost always write code that assumes NULL == 0 and be right provided you do not actually test NULL == 0.
Friday 20th will be my last day at Moodstocks. I am leaving a company where, after three years and a half, I was the most senior employee. As you may imagine, it was not an easy decision.
Moodstocks has grown up since I joined it. After several pivots, the founders have assembled a great team I will miss, and together I dare say we have advanced the state of mobile image recognition. Recently, we have also been looking at other technologies related to mobile.
We have released two new products. The first one is what was known as Physalis and took the commercial name Winch. This is what I have been mostly involved with for the last few months. I believe it has the potential to become the reference solution to write native mobile applications that react quickly and work offline.
The second product is Overlay, a mobile application that lets you buy products from paper catalogs online. Years after Pikadeo and Notes, it is finally time for a real application available in the stores, leveraging Moodstocks’ image recognition SDK and Winch, showcasing the best of both technologies. But beyond pure tech, it is also a demonstration of Augmented Reality as we like it: fast, predictable, purposeful. No need for 3D models.
With those two products in the pipes, choosing to leave was incredibly difficult. But I did nevertheless, because of an opportunity I could not turn down.
So, what next for me? After two weeks to see my family, eat too much and have some welcome rest, I will be joining the team developing the Lima as a core developer.
The Lima, which promises no less than to solve the personal data storage problem, is the first hardware product I backed on Kickstarter. I was thinking: “this won’t be easy, but if those guys can do it, I want that device.”
The Kickstarter went (very) well, so they started looking for help and got in touch. I was curious and decided to meet the founders. After a Saturday morning spent discussing the project at Starbucks, I was convinced: they are the right people to do this.
For years I have been convinced that ubiquitous computing is one of the next big things in technology and wanted to be part of it. As I see it, Lima gives me this opportunity today.
So it looks like my short-term future holds some Unix development and a good dusting of my rusty C. I may try to sneak a Lua interpreter in that box at some point, but please don’t tell the others! ;) Severin and Gawen have high expectations and I do not expect much relaxing but hey, it should be fun to live the adventure of Early Stage again.
I use different programming languages for different tasks, but the one I prefer is Lua. I have always wanted to use it for the Web, and in a way I already do: this blog is a static website generated by a custom Lua program. I have also written several services that can speak HTTP+JSON in Lua. For larger, HTML-based Web applications however, I have never found the framework I wanted. I have tried several of them, but kept coming back to more dependable platforms.
Last March, I gave Lapis a try. It is a relatively new framework written by Leaf Corcoran, the author of MoonScript, a programming language that compiles to Lua (like CoffeeScript for JavaScript). Lapis is powered by OpenResty, an incredibly fast web application server that run inside the nginx Web server and is already used by large websites like Taobao and CloudFlare.
Lapis was built with MoonScript in mind, so I had to hack around it to make it work with plain Lua. It worked but was too verbose, so I eventually gave up on that and on Lapis altogether. But that was Lapis version 0.0.1! Leaf continues to improve it and recently released version 0.0.4. Last Friday evening, on a train to Bordeaux, I decided to give it another try.
Lapis now natively supports Lua and has improved in various aspects. I have found it comfortable to write the views and the configuration file with the MoonScript DSLs provided by the framework. For logic (models and controllers) I used Lua directly since I prefer its syntax to MoonScript for regular code.
I have published a small skeleton application which demonstrates this dual languages style to GitHub. I chose to use Redis for the datastore because I know it well and had it running on my laptop, so I did not use the database integration layer of MoonScript which is designed primarily for PostgreSQL (but it looks nice as well). This application is a kind of Hello World but it demonstrates most features of the framework, including sub-applications, widgets, layouts, exception-handling and input validation.
If you want an example of a larger Lapis codebase with a different style, MoonRocks is written in Lapis and its code is on GitHub.
So, what did I think of it? Well, finally I could see myself write a serious Web application in Lua! I will still choose Python and Flask in a professional setting because it is is a more stable, more feature-complete stack, and because I would not want to ask a whole team to learn both Lua and MoonScript to work on the project. But if I make a Web application as a personal side project, I will certainly try to use Lapis for that.
Some of the code I write is Open Source, but these days most of it is closed source and property of Moodstocks, the startup I work for. For the last three years I have had the chance to work on a lot of really interesting projects, the most significant of which I will talk about now. If you are still a student, maybe that can inspire you to join a startup. Maybe you will even decide to join us in our quest to advance mobile image recognition and applications in general.
When I joined Moodstocks in April 2010 the team was working on a mobile price comparison application called Pikadeo. The pitch was that you could take a picture of any cultural product (CD, DVD, book…) and it would give you a list of places where you could buy it, sorted by price.
The iOS application itself, largely designed by Louis Romero who had interned at Moodstocks and left just as I arrived, was working. The image recognition technology was working too, although it was purely server-side. The team was already researching how to leverage client-side processing but it was really just a crazy idea at that point, so Pikadeo was doing what most “mobile” image recognition software still does: send JPEG frames to the server.
What was missing was the data. We needed to crawl several large e-commerce websites, extract product images and metadata, send the former to the image recognition engine and the store the latter in a database. So I set out to write a crawler in Ruby, which was the dynamic language of choice of the team at the time. Moodstocks was a Ruby / C++ shop due to the background of the founders. Obviously things have changed a lot since then.
I tried to use Hadoop for the job, mostly because it was trending at that time and I had access to Amazon’s Elastic MapReduce. I soon understood that 1) the Hadoop Streaming interface was not quite there yet so I would have to switch to Java and 2) the Map/Reduce paradigm was not the best for the job anyway.
After reading a few papers on crawling (I had to anyway, since that project would be the basis of my MSc thesis, but it actually helped a lot) I ended up writing a kind of Master/Worker system, with work queues in Beanstalkd and metadata storage in Amazon’s SimpleDB, which did the job. It did the job a little too well, actually, since it ended up DDoSing an e-commerce website for a few seconds during a performance test for my thesis. Fortunately I was monitoring it and hit the stop button…
After setting reasonable speed limits and balancing the requests between various websites, Harvest was fast enough for our needs. The bottleneck became the image search engine itself, I will expand on that later.
Due to the deprecation of the Pikadeo product, Harvest is no longer used today. It is probably a good thing: it was my first Ruby program so the code was awful, it was way too complex and too tied to AWS (the master instance would run and kill worker instances, it relied a lot on SimpleDB…). That being said, the crawling model was sound.
Once we got crawling sorted out, the image recognition engine itself became the problem. Oak (it has become a tradition to use plant-related names for our projects internally) had been almost entirely written by Cédric, Moodstocks’ CTO. It was a piece of C++ software, with the image recognition parts isolated in dynamic libraries and a Thrift layer to interface with the core Ruby on Rails Web application. It was multithreaded, designed to run on a single multicore EC2 instance.
The scalability pain point, it turned out, was not CPU load. The index was stored in a B+ Tree in Tokyo Cabinet, a tool we like a lot and still use today in other parts of our system. The problem was that when we indexed millions of images the dataset would inevitably become very large, larger than the available memory. The system would still be very responsive on most reads, but writes would invalidate large chunks of the in-memory cache and result in long pauses.
Latency is the enemy when you write image recognition software, so we decided not to sacrifice it: all the index had to fit in RAM. We decided to consider RAM as our primary datastore. That decision would bring about our later choices.
Since rebuilding an index can be very long we wanted something that could persist even if the engine crashed or had to be restarted for an update. Soon it became obvious that Redis could be the answer. However it was missing some commands that we needed, especially one that would insert the same key with different values in different maps (if you have already written inverted indices you may understand why, otherwise have a look at that presentation). Lua scripting was what we needed, but it wasn’t there yet so I ended up forking Redis to develop it in C while lobbying for scripting support. Acorn was, to my knowledge, the first application to run Redis 2.5 in production, and the first one to use Redis scripting too. We never encountered any Redis-related crash.
Now, to have the index fit entirely in RAM, we would have to distribute it across different machines, so Acorn would have to be a distributed system. Knowing that, I chose to make Acorn nodes single-threaded: they would communicate by message passing and we would have several of them per instance.
We chose MessagePack for serialization of Redis values, and I started looking at MessagePack-RPC. It had a lot of the pieces that I wanted for the distribution part, and one major problem: it was only usable in Ruby. But we were not CPU-bound… Would it be sensible to write the engine in a dynamic language? I started investigating that possibility. Our C++ libraries already had Ruby bindings that we used for vision R&D, and the little number crunching Oak Core did (mostly different scoring algorithms) turned out to be fast enough in Ruby.
So Acorn ended up as a distributed system written in Ruby, with MessagePack-RPC for communication and a fork of the development branch of Redis at its heart. It used MessagePack-RPC for communication with our Rails stack too.
In retrospect, relying on two unstable pieces of software was risky. It turned out well, and Redis was definitely the right choice, especially since Lua Scripting now allows us to use a regular, stable 2.6 version. 0MQ would probably have been a better technical choice than MessagePack-RPC, and plain C better than Ruby, but I believe those choices saved us development time, and time to market was important. Acorn is still in production today, doing its job for legacy clients who use online recognition.
You now know that when I joined Moodstocks it was trying to be a B2C company. However we were seeing interest in licensing our technology, and began to envision a B2B product: Moodstocks API.
When you write an API, especially as a product, you must write applications for it simultaneously. They serve two purposes: demonstrate what your API can do, and help you figure out how it should be improved. We set out to do that with two mobile applications, one of which was Notes.
The original idea I proposed was, I think, simple: Google SideWiki (RIP) for the real world. That is: you walk in the street, you see something interesting, you take a picture of it, you get a comments thread. If you are the first to do so, you get to leave the first comment (yay, first!!1).
As we were looking to add virality to it, that idea developed into a kind of mostly mobile social network where both people and objects could be followed. Objects actually had their own timeline with an associated Atom feed, which you could reach by browsing or, of course, as the result of an image search.
Technically the server-side part of Notes was a rather classic Sinatra application. The most interesting part of it was that it used some kind of CQRS architecture with all reads coming from Redis and all writes going to log-structured storage. The very nice thing about it was that any part of it could be replayed so it was almost trivial to reproduce bugs or replicate production incrementally to a development setup.
The iPhone application, on the other hand, was one of the nicest and most complex ones we have ever written. I wasn’t responsible for it so I won’t get into details here but the latest internal version we never actually released was IMO a thing of beauty.
As it turned out, Notes got a reasonable amount of online press after our CEO showed it to Michael Arrington at the Le Web conference. This got us a few users and we briefly thought about making it a product in its own right. I wrote a wxPython GUI to analyze logs, trying my hand for the first time at techniques like cohort analysis.
Eventually we took the decision not to invest more time in the idea: we were a small team and our now core B2B business needed our attention. Notes’ success was a long shot and would have required significant time and money investment so I guess it was the right decision, although I would love to see someone revisit the idea.
As I said, Notes was written to help us design our API. Using its feedback and that from the few users of our v1 API, which was more some kind of beta, I set out to write version two.
I will not expand too much on all its aspects here, REST-ish API design being well covered in the literature and online (start here).
The main differences with API v1 were the use of JSON instead of XML, and the ability to index a single image by uploading it to the API using multipart post. Previously, users would upload a XML list of image URLs and associated IDs; we would download them and tell you when indexing was over. Now users index single images and changes are taken into account instantly. The necessity for that was a lesson from Notes and user feedback, and it was made possible by Acorn.
Another interesting choice was the authentication method, HTTP Digest, which we kept from version one. Theoretically, it had all the right properties and was a standard, so it was the best choice. What we had not realized is how many implementations were broken or incomplete (i.e. not supporting nonce reuse, which is a necessity on mobile to reduce the number of HTTP requests). I ended up having to submit patches to a lot of them, and I am not even mentioning .NET land… If I had to do it again today I would probably go with Basic Auth and SSL.
Earlier, I wrote about how I had made Acorn processes single-threaded. This had some advantages, but also a big inconvenient.
Part of the image search process involves quantizing features, which means associating vectors in a many-dimensional space to integers. To do this the curse of dimensionality forces you to use an approximate nearest-neighbor search algorithm.
The way it works is: take a large number of features from a representative dataset and use some kind of clustering algorithm (e.g. k-means) on them to obtain a bunch of centroids (a “vocabulary”), then process these centroids to obtain a datastructure called kd-forest which will be used to perform nearest-neighbor search (a “dictionary”).
Vocabulary generation is clearly an offline task that requires a lot of number crunching and is done as little as possible. Generating the kd-forest, on the other hand, takes from a handful of seconds to a few minutes depending on the size of the vocabulary, so it is frequently done on engine startup. The kd-tree itself only exists in RAM.
The problem with that was that a kd-forest is a rather large datastructure. In our case it occupied hundreds of MB of RAM and took about one minute to generate. That was OK with Oak, where it was shared between threads, but with Acorn that overhead had to be paid for every process, both in space and time. We had to find a way to share the kd-forest across Acorn nodes on the same machine, and if possible to make startup faster.
The solution I opted for was to rewrite the whole quantizer. Previously we had been using popular Open Source libraries for this, but they didn’t do what I wanted.
I wrote the kd-forest generation algorithm as a LuaJIT program. It was the first Lua program officially used in production at Moodstocks, although as you will see it was only run offline. What it does is take centroids as input, generate a kd-forest and serialize it in a way easily readable in C thanks to the FFI. It can also actually perform nearest-neighbor searches but this is only used for test purpose.
Once the kd-forest is serialized, it can be loaded into system shared memory quite fast. A C library can then be used in every Acorn process to access this shared memory read-only and perform nearest-neighbor searches.
The idea is simple once you stop under-estimating the capabilities of SHM on Linux. By default it usually limited to a few MB so you have to increase it a lot for this to work (it can be done with sysctl). The implementation, on the other hand, is far from trivial. My code uses a lot of pointer arithmetics, I should probably clean it up someday, but in the meantime it does its job perfectly.
The Acorn Quantizer was the last major improvement to our online search stack. Around that time, we resolved on a major technological shift: we would perform image recognition on mobile devices directly instead of doing it on the server. Of course, initially, we would have an hybrid approach where on-device recognition would work as a kind of cache, but the mobile was where we would focus our efforts.
Doing on-device image recognition, though, almost meant starting from scratch: we had to make different technological trade-offs, use very different algorithms, and that meant writing an almost entirely new image recognition stack. We named that project Seed.
Seed encompasses a lot of things now, but at its core are proprietary Computer Vision algorithms that we set out to develop with Cédric and Maxime, who had joined us by then. We would discuss them as a team, then Maxime and Cédric would implement them in C while I would work on a Lua version.
The big picture is that some processing is done on the server at indexing time to generate signatures which are then sent to the client. Server-side software used to be entirely Lua, client-side software entirely C, but we decided to implement the whole stack in both languages. I think that was one of the best ideas we ever had. Being able to compare results avoided errors on both sides (tricky things like off-by-ones were always noticed thanks to the fact that Lua is 1-based, floating-point math issues were found…). Lua allowed faster prototyping on some parts and it was interesting to compare the different architectural choices we were making.
With the current (second) generation of the Seed algorithms, we are actually mostly using the C implementation through the LuaJIT FFI on the server side now. That is because I have been working on other projects while the rest of the team (which is not as comfortable with Lua) was developing them, so I would have been a bottleneck if we had kept the dual stack approach. I may well bring the Lua branch up to date someday though, who knows?
Moodstocks’ server-side architecture is some kind of SOA. That means we have a lot of different services that run as daemons and need to stay up. chksrv is a medium-sized program in Bash that takes care of this. It is deployed on every instance with a configuration file that indicates which services should be running on that instance, and it makes sure that they are (correctly). It also checks if other instances are up. If something goes wrongs, it warns the “ops team”, who is basically me and Cédric as a backup in case I am not available.
chksrv is a very useful piece of software but I was a bit worried by its growth as we added services. Standardizing the way we deamonize processes helped a lot with that by increasing code reuse (thank you libslack).
chkcoherence is the ideal complement to chksrv: where the latter checks if services are running, the former verifies that they are doing things right. It is also written in Bash at the top level. I have already written about its concept here.
Anemone is the project that deals with everything related to metrics and measurements at Moodstocks. It is written in Lua and has quite a few different roles:
It also has a web-based dashboard for the team with high-level KPI, written in JavaScript and flot. Someday I might integrate Brett Slatkin’s Cohort Visualizer into it.
I said earlier that with Seed we generate image signatures on the server and send them to the mobile clients where they are used for recognition. Dandelion is the code name of the service responsible for that.
It turns out efficiently sending millions of image signatures per day, over slow and unreliable networks, to devices everywhere in the world, is not trivial. So Dandelion, more than software, is a synchronization protocol and its implementation; a range of tricks to make the best of mobile networks packaged as software. It is one of the reasons (along with all the innovation on CV algorithms and their optimized client-side implementation) why we can propose client-side recognition with databases of thousands of images or even videos, an order of magnitude more than our competitors.
The server part of Dandelion is written in Lua and depends on pieces like Redis and Beanstalkd, which is why I wrote haricot.
Finally, Physalis is the project I am currently working on. It has not been released yet so I won’t get into the details, but I can explain the reasoning behind it.
While we were building Dandelion and through our experience with our clients, we learned the following things:
So we thought: we have done it, why not make it accessible to everybody? This is what Physalis is: Moodstocks’ image signature distribution system generalized so that you can leverage it for your own mobile application.
Physalis will be available in private alpha for selected users soon, under its real brand name (Physalis is only its “internal plant-themed name”). If you are interested in trying it out, get in touch. The requirements are that you should be making a mobile application and ready to communicate on a regular basis with us: we are doing this alpha to collect useful feedback.
EDIT: Physalis was eventually released in August 2013 as Winch.
Recently I found a rare, ugly bug in a piece of software that had been in production at Moodstocks for 6 months. The bug itself is not that interesting, but the way I found and fixed it is.
Imagine you have a database where you can index some documents in a full text search engine. Indexing can be turned on and off on a per document basis.
As the size of your dataset increases, you outgrow it and decide to use a separate service for search. So you change your code such that documents which must be indexed are sent to that service, and you add a boolean field in your main datastore to indicate whether the document is indexed or not.
All is fine until one day you refactor this code a bit too fast and part of the logic for document updates becomes something like this:
if document.indexed then
search_engine:index(document)
document.indexed = false -- <-- WAT?
end
Of course, in practice, it is less trivial and only happens in rare corner cases, so your tests don’t catch it…
This kind of bug in a system can be very nasty. What happens when it is triggered is that the worldviews of two subsystems (here the datastore and the index) are not coherent anymore.
If you are a programmer not used to distributed systems, you may think that the problem is that the information “the document is indexed” is duplicated. State is bad, but duplicate state is plain wrong, just always ask the index for that information and drop that “indexed” field!
In a normal application setting you would be right, but this is one of the main differences between SOA and OOP. There are two reasons why you do not want to do that. The first one is performance: this may generate more internal network requests. Its importance could be discussed at length (“Is it some form of premature optimization?”).
The second reason is much more important though: if you do that, your index becomes a data-critical service. That means you cannot lose its state without losing information, so you have to back it up seriously. This simple boolean field in the datastore is enough to rebuild the whole index, making it non-critical.
So that leaves us with denormalized data and its own problems. How do we mitigate them?
Once you have denormalized data, your problem is to keep it coherent. That means that there are invariants that must be verified at all times by the various states of the subsystems. Or rather, because of asynchronous jobs, invariants that shouldn’t remain unverified for too long.
Those invariants are almost always properties on sets. For instance, if you have a forum where only registered users can comment, users who have commented must be a subset of users who have confirmed their email. In my case, the set of documents indexed in the search engine must be equal to the set of documents flagged as indexed in the main datastore.
The big idea is that every time you denormalize data you should write invariants that ensure coherence. These invariants are checked by scripts that can be run at every transaction in some cases, but are more usually cronned. You should also have procedures to reconcile (repair) the data in case of incoherence. I am not a huge fan of having them run automatically: incoherence often reveals bugs, so humans should check where it comes from and fix it.
In our case, the script responsible for coherence checks warned me that a few documents belonging to the same user were present in the search engine but not flagged as indexed. I asked the application logs what had happened to these documents around the time when the incoherence occurred, and saw they had all been updated. I looked up the relevant code path in application code… and facepalm-ed. I had pushed that code to production half a year ago, and it was obviously wrong.
The moral of it all is not, as I already discussed, that you should not denormalize data. It is not that we should write more tests, either. At least it is not what I want the takeaway to be (we do test these things more carefully now, but edge cases can always slip through).
What I think this story shows is that, if you write distributed systems that handle denormalized data, you should have:
I cannot imagine releasing a distributed system that does not have those things. They are even more important than a comprehensive test suite to me. Moreover, the coherence checks can also be run in the test suite itself (on mocks for instance) so writing them is always a win-win.
I found a talk by Werner Vogels, the CTO of Amazon who is a role model of mine, on 21st century application architectures.
After his presentation, someone from the audience asks him what skills he is looking for to run these services. His answer (starting at about 40:45 in the video) is so interesting that I thought it would be worth it to write it down. Here it is:
What kind of skillset am I looking for for people to build these [infrastructure services] and manage them? I think I’m looking for a set of standard things that we look for in every Amazonian PM, which is…
A very strong sense of ownership. This is: the stuff that you’re building is not something that your boss tells you to build. You have to take pride in what you do, and you have to be someone [who] “does not like his or her own smell”. You have to be [highly] self-critical.
[You] have to be able to disagree but be able to commit to actual decisions being made, things like that. There’s this set of what we call Amazon leadership skills that I would be looking for in any engineer that we hire.
[In many] of these services we’re looking for people with very good distributed systems skills, or at least with very good fundamental distributed systems skills which they have learned in school. Actually, it isn’t that you have to have built a distributed system when you were in school. Remember: no professor ever had a real job in their life, right? And so they didn’t teach you how to build systems. But to have a truly fundamental understanding of how to read fault-tolerant algorithms, of how to think in a fundamental way about scaling… and those kind of things, is something that we are absolutely looking for.
Some demonstration, if you’re just out of school, [that] you’ve been involved with some practicals. Have you contributed to an Open Source project? Are you involved with programming groups? We’re looking for someone that is actually capable of doing hands-on work. […]
If you’re really more experienced, what we will be looking for… Do we look for particular programming skills, languages? Not really. Most people are able to program in Java, C++… I don’t think I’ve actually met people who are not able to do that. We are in an environment that is pretty unique in that sense.
We don’t mandate the use of particular programming languages, we don’t mandate the use of particular middleware pieces or things like that. We believe that our systems, our team should be moving as fast as possible, which means that if we hire the absolute best people they should be able to make the decisions, what the right tools are for them to use in their particular project.
It doesn’t always go well. I think we’ve had a few choices around Erlang which did not necessarily work out that well, not because of Erlang, but because it is actually really hard to hire Erlang programmers after that. So we expect our engineers to have some level of business sense.
I think the willingness to do operational work… We live and breathe the DevOps mentality, where our engineers are responsible for the software that they run at some level, whether that is part time or whether that is actually in full site with some of our other engineers. We strongly believe that there’s no bigger motivator to fix your software than beepers going off at 4 A.M. And we give you time to actually fix your services if that’s the case.
These are the first few things that come to mind.
Let us consider a system that can take a finite number of states S[1] to S[n] over time.
We can represent the system as a N-node directed graph, where nodes are the states. An arc exists between node S[i] and S[j] if the system can transition from S[i] to S[j]. You probably already know that.
We will now observe the evolution of the system over time. We will model time discretely. That does not mean that time itself is discrete, but we will observe the system at fixed-interval “ticks”. We call S(t) the state of the system at tick t.
If the system is known to be in S[i] at tick t (i.e. S(t) = S[i]) we call P(i->j) the probability that it will be in S[j] at tick t+1. Here is a definition for the mathematically inclined: P(i->j) = P(S(t+1) = S[j] | S(t) = S[i]).
A few remarks:
P(i->j) is considered independent of t;
P(i->i) exists;
∀i, ∑[j=1,N]{P(i->j)} = 1.
By the way, this is called a Markov Chain.
Now let’s complicate things a little by introducing a separate system that can take states V[1] to V[M]. This second system V is tied to S: at every tick t, the probability distribution of V(t) only depends on S(t). We will note: O(i,j) = P(V[j] | S[i]).
You can think of V as something that observes S and reports on its state in an incomplete and probabilistic manner.
If you have understood everything up to now, you should easily get what this means: ∀i, ∑[j=1,M]{O(i,j)} = 1.
If you have not, let’s take a really simple example with N = M = 2. You can imagine that both S and V are lights that can be either red or blue. The relationship between S and V can be expressed by something like this: “If light S is red, then there is 90% chance that V is red too. If light S is blue, then there is only 40% chance that V is red.”
Now, add more possible colors to S and V, not necessarily the same number for both, to generalize the system.
Now, here is our problem: imagine that you know how system S works (the probabilities of transition between the states) but that it is hidden in a black box such that you cannot observe it. What you can observe is system V. Is there any way to guess what system S is doing at any time in this setting? If yes, how?
The answer to the first question is: “sometimes, yes”. “Sometimes” means “if the probability distributions help us”.
The second question is the one my favorite algorithm, the Viterbi algorithm, answers. Let’s see how it works.
First we need to represent the problem in a way that can be understood by a computer. I will use the Lua programming language for that.
We start by defining a few parameters. We will choose N = 3 and M = 2 for simplicity. We will observe the system over 1000 ticks.
local N = 3
local M = 2
local L = 1000
The probabilities of transitions in S will be represented by a NxN matrix, with P(i->j) as T[i][j]. We use percentages because they are easier to read and avoid some floating point calculation issues.
local T = {
{ 60, 20, 20 },
{ 0, 70, 30 },
{ 70, 10, 20 },
}
For instance, there is a 70% chance to transition from S[3] to S[1].
The probablitities of observations will be represented by a NxM matrix, with O(i,j) as O[i][j].
local O = {
{ 5, 95 },
{ 55, 45 },
{ 90, 10 },
}
Let us make sure we didn’t make too many mistakes thanks to our two probability equations from earlier:
local sum = function(t)
local s = 0
for i=1,#t do s = s + t[i] end
return s
end
for i=1,N do
assert(sum(T[i]) == 100)
assert(sum(O[i]) == 100)
end
This is what our example system looks like in graph form:

The green part is the Markov chain itself, the blue part corresponds to the observation device. Arcs are labeled with probabilities of transition or observation.
Now we can simulate the behavior of the system.
local proba_pick = function(v)
local p,s = math.random(1,100),0
for i=1,#v do
s = s + v[i]
if p <= s then
return i
end
end
assert(false)
end
local S,V = {},{}
local transition = function(t)
assert(S[t-1] and not S[t])
S[t] = proba_pick(P[S[t-1]])
end
local observe = function(t)
assert(S[t] and not V[t])
V[t] = proba_pick(O[S[t]])
end
S[1] = math.random(1,N)
observe(1)
for t=2,L do
transition(t)
observe(t)
end
This code is slightly more complicated, but what you should understand is that, at the end of it, S is a vector of L states of the system and V is the corresponding vector of observations. The initial state S[1] is chosen randomly with uniform probabilities.
Now, let us proceed to guess what happens inside the black box. We do so iteratively: at each tick t, we calculate the most plausible sequence of events that could have led to each possible state of the system, given the observation at t and the results for t-1.
local trellis = {{}}
local prb = function(x)
return math.log(x/100)
end
local viterbi_node = function(t,i)
local prev = assert(trellis[t-1])
local idx,proba = 0,-math.huge
local p
for j=1,N do
p = prev[j].proba + prb(T[j][i]) + prb(1/N*O[i][V[t]])
if p > proba then proba,idx = p,j end
end
return {
state = i,
proba = proba,
prev = prev[idx],
}
end
local viterbi_step = function(t)
assert(not trellis[t])
trellis[t] = {}
for i=1,N do
trellis[t][i] = viterbi_node(t,i)
end
end
-- initialize the trellis
for i=1,N do
trellis[1][i] = {
state = i,
proba = prb(1/N*O[i][V[1]])
}
end
-- run the algorithm
for t=2,L do
viterbi_step(t)
end
Again, not trivial code, but this is the Viterbi algorithm itself. Moreover we use logarithmic sums for probabilities to avoid numerical problems.
What the algorithm does is build a trellis, a special kind of graph. For every step t we calculate the most plausible sequence of events (a path through the trellis) which ends up with the system in each of the possible states. When we reach the end of the simulation, we take the most plausible state of the system and take the path that led to it as our estimate of what happened.
To avoid memory usage explosion, we do not actually store the paths themselves in the nodes of the trellis. Instead, in a node at tick t, we store a pointer to the previous node (at t-1) that led there. This is why we have to backtrack through the trellis to find the actual path:
local getpath = function(node)
local r = {}
repeat
table.insert(r,1,node.state)
node = node.prev
until (not node)
return r
end
local best_node = function(nodes)
local r = {proba = -math.huge}
for i=1,#nodes do
if nodes[i].proba > r.proba then
r = nodes[i]
end
end
return r
end
local bestpath = getpath(best_node(trellis[L]))
To check if this work, let’s take the actual system out of the box and calculate how much it looks like our estimate:
local ok = 0
for i=1,L do
if S[i] == bestpath[i] then
ok = ok + 1
end
end
print(100*ok/L)
The result I got with those parameters is 72% accuracy, to compare to what a naive random process would get (33%).
Let’s go back to the title of this post: now that you know what it does, why is Viterbi’s algorithm my favorite?
I admit that the elegance of the representation of the problem as a trellis is part of the reason, but mainly it’s because of its implications. Think about it: fundamentally it is an algorithm that allows a machine to explain what it observes. Isn’t that crazy?
Yes, it is. And because of that it has a whole range of applications, from advanced orthographic correction and speech-to-text to the decoding of convolutional codes used in voice codecs. And I suspect its potential has not been fully exploited yet.
For a long time I have thought that there were only two fundamental parts to Computer Science: Algorithms and Data Structures. This is how it used to be taught, and how I think it should still be taught to beginners.
There are two main categories of parts in a computer: processing units (CPUs, GPUs…) and data storage units (caches, RAM, disk…). Your typical information processing program takes data from a durable storage unit, moves it to a temporary storage unit, modifies it thanks to a processing unit and stores the result in a permanent storage unit. Of course there are variations around this theme, and some programs do a bit of extra work such as handling user input, but that is the idea.
We call “algorithms” the different ways we instruct the processing units to act on the data and “data structures” the different layouts we use to store data in the storage units.
A very simple view of fundamental CS could be summed up to these two disciplines. You may argue that other things are indispensable, for instance theoretical aspects like lambda calculus or Turing machines, or maybe paradigms. You would be right, these things are important, but if you really want the baseline, it all comes down to algorithms and data structures.
Or so I thought.
It turns out looking at the aforementioned parts of the computer is not enough. We need to look at the negative space too: how does the data move around? Inside the computer, data mostly circulates in buses. Between computers, data may circulate in networks, on detachable storage units…
With computers becoming increasingly parallel, the Internet reaching ubiquity and programs turning into complex distributed systems, understanding the interactions between computer parts, software parts or entirely different systems has become a necessity. This reveals a third fundamental side of CS, on equal footing with algorithms and data structures: protocols.
Protocols are more present in CS than you may think. Even the original vision of OOP by Alan Kay was mostly about protocols. Joe Armstrong, a father of Erlang, recently insisted on the necessity to teach protocols (and algorithms - if he had added data structures this post would look like plagiarism ;p).
CS classes at engineering school did not teach me much or influence the way I write programs, but majoring in network engineering did. It made me start to think in terms of independent blocks and the protocols they use to communicate. I cultivated that vision during my MSc in distributed systems (it used to be called “grid computing”) and I still have it today.
So when I see people arguing endlessly about static versus dynamic typing or such matters, I think: OK, that may be important, but do not forget CS is, at heart, about algorithms, data structures and protocols.
When people say SOA (Service Oriented Architecture) they often mean different things. My personal definition of SOA is rather loose and, like all the quotes in this article, comes from Werner Vogels, the CTO of Amazon:
For us service orientation means encapsulating data with the business logic that operates on the data, with the only access through a published service interface.
If you are an Object Oriented Programming person this should ring a bell. Indeed, SOA is essentially encapsulation at the system level. Actually, I strongly believe OOP is a good idea (encapsulation) applied at the wrong layer.
The opposite of SOA in the context of the Web is monolithic applications. Think textbook Ruby on Rails or Django, for instance. Monolithic applications are fine as long as they are small, but I think SOA is a better fit for larger projects. If you are a technological startup, that means you should at least start thinking about what SOA could bring you if you are starting to have a clear idea of what your product is. Monolithic applications are great for prototypes and MVPs.
If you are wondering if what you are currently doing is closer to SOA than to monolithic applications, ask yourself these questions:
How many different repositories does our code live in? (If you do not use source control you have more urgent problems to think about than SOA.)
How many different databases do we have? How many database instances do we have? How many connections? How many schemas? (SOA and NoSQL tend to mix well.)
Do we have a Web application that does significantly more than rendering templates? Is it stateful? What parts of that state could we also use in a mobile application? Does the same application render JSON and HTML?
One of the most obvious benefits of SOA is that services are easier to scale than monolithic applications. Maybe you want to migrate user authentication data to Redis and keep other data in whatever DB you already have? SOA makes it trivial. Maybe some part of your product would benefit from running on an AWS instance that has lots of RAM while another one is CPU-intensive? Just deploy the corresponding services where they run best.
SOA will also help you achieve resilience. You can program user-facing or aggregate services defensively so that they detect the unavailability of backend services and degrade gracefully. You can even use a chaos monkey to ensure this.
However in my opinion the main advantages of SOA are not technical, they are organizational.
As the number of your employees increases you will have to separate them into teams. The classical way to do this is by specialty: first developers and operations, then spin off DBAs… The main problem with that organization is that it hinders innovation. To create a new feature you need to coordinate all these people, and it results in endless meetings and sterile debates (not to mention clan wars).
This is not a fatality. You have probably heard of DevOps, and maybe dismissed it as Yet Another Buzzword. Well SOA is the easiest way to implement True DevOps (TM?), meaning that developers and operations really work together all the time. The trick is that there are no “devs” and “ops”, because you create teams along another dimension: services. In Werner Vogel’s words:
The traditional model is that you take your software to the wall that separates development and operations, and throw it over and then forget about it. Not at Amazon. You build it, you run it. This brings developers into contact with the day-to-day operation of their software.
These teams are like smaller startups within your growing startup: they have all they need to build new features or even whole products by themselves. And they will keep you nimble if you give them enough leeway:
We allow teams to function as independently as possible. Developers are like artists; they produce their best work if they have the freedom to do so, but they need good tools.
SOA also gives you language agnosticism almost for free. Not much prevents different services from being written in different programming languages. Why would you want to do that? Well, beyond the fact that different problems are better solved in Python, Haskell or C, there is lot of talent out there not using Ruby, Python, PHP, Scala, JavaScript or whatever language you have chosen for your monolithic application. Some of those Scheme, Io, Factor, Lua or F# hackers can be real assets for those clever enough to give them a job.
Finally, I can see a (perhaps more abstract) last advantage to SOA which is related to the rise of the Programmable Web. After all, there is little difference between a Web API and a service, except that you wrote the latter and not the former. Embracing SOA will prepare you to work with external APIs, and more importantly to publish your own. For instance, lots of AWS services have started their life as internal services at Amazon.
I am not saying SOA only has advantages. Such an architecture can be fragile if you do not code with service unavailability in mind. Testing complex operations involving lots of services can be hard, and debugging them even harder. Correctly defining the boundaries and interfaces of services requires a significant amount of reflection and may not be a good idea when you do not know what exactly you are building.
That being said I still think more technological and/or Web startups should consider SOA instead of the “everything within the Framework” approach, not really for future scalability reasons, but because they risk limiting their flexibility, speed of iteration and capacity to hire short term if it is not already the case.
I have not had a blog for over two years. The main reason for that is that writing is hard, so I went shopping on Twitter instead.
This was a bit hypocrite since I am an advocate for the Open Web and a huge consumer of information feeds, with several hundreds of them in my news reader. So for this New Year I have decided to put an end to this situation, thrown together a few lines of Lua code that act as a static blog generator (because reinventing the wheel is fun), and here it is: my new online journal.
I do not know with what frequency I will publish but expect articles about working at startups, distributed systems, programming in Lua, dealing with mobile networks, and whatever I happen to read and find interesting. Oh, and also the occasional rant, I guess.
Now here’s to the tradition:
local greet = function(name)
print(string.format("Hello, %s!",name))
end
greet("World")
I used to enjoy predicting how computing technology would evolve; it was like a game for me, and I was not too bad at it. But for a few years I have felt like I could not predict anything anymore. Things went too fast, and that bothered me.
During these years I have observed things that, taken separately, did not seem to make much sense. Some that already existed and grew in importance, and some that were new. Mobile & Local computing. The Internet of Things. ARM’s revenge on x86. The Cloud. Multicore, GPGPU. The HTML5 applications craze. Unhosted. Siri. Minix3 getting funding. Wolfram|Alpha, NKS. JSON, Git, CouchDB, Redis, Riak, DynamoDB, Doozer. JIT compilers, functional languages, event-driven programming. SPDY, 0MQ, MessagePack-RPC. Mechanical Turk and humans computer pieces. LTE, 4G networks, Free disrupting the French cell phone industry.
And then, a few months ago, I took a step back and I found a pattern. If I am right, all this is going somewhere, and that somewhere is ubiquitous computing. But not in a limited sense: “computation everywhere”, taken to the extreme. My presentation slide-show moving from my pocket to the overhead projector of the conference room, forking to the tablets of the attendees. Computation jumping from my desktop computer at work to my cell phone, sitting there peacefully to save battery while I walk to my self-driving car, only to rush to the powerful computer in the dashboard. Software running in the Cloud, on the 1028 cores of my DSL box and my toaster at home, in my headphones and in my AR lens, all simultaneously. One application in several (lots of) places. Ubiquitous.
Now maybe you are thinking “he’s nuts”, and I am thinking “maybe they’re right”. Because for this to work we will have to solve what I consider the hardest problem in Computer Science: large-scale algorithm collaboration. I honestly do not know how long it will take us or even whether it is within the reach of our limited brains. But if I am right, we are going there, and this is an exciting destination. This is a journey that requires planning, and going now would be foolish, but when we will be ready to set sail towards these unknown lands, count me on board of the first ship.
Tout le monde parle de neutralité du Net en ce moment. J’ai pensé que ça pourrait être une bonne idée de faire un point là-dessus, parce que j’entends et je lis tout et n’importe quoi, en particulier dans la presse française.
La première étape pour appréhender le problème de la neutralité du Net, c’est de la définir. Benjamin Bayart s’y est essayé devant l’ARCEP et je suis d’accord avec lui pour l’essentiel, même si nos points de vue divergent probablement sur certains points de détail.
D’abord, il faut comprendre que la neutralité du Net est une qualité essentielle. J’entends par là qu’Internet est par définition neutre, et donc qu’un réseau non-neutre n’est pas Internet
Qu’est-ce qu’Internet ? Comme son nom l’indique, c’est un ensemble de réseaux qui se sont mis d’accord pour se regrouper et permettre aux entités qui les composent de discuter entre elles. Que les puristes me pardonnent les raccourcis techniques qui vont suivre.
Les réseaux qui composent Internet sont appelés systèmes autonomes (AS), parce que leurs dirigeants sont libres de définir la manière dont les messages sont transportés en leur sein ainsi que les règles utilisées pour que ces messages arrivent à bonne destination. Pour faire une analogie fréquente : peu importe si les facteurs du bureau de poste de Trifouilly-les-oies distribuent le courrier à bicyclette ou en scooter, et peu importe s’ils utilisent un plan du quartier ou leur GPS pour trouver le 47 rue des Lilas, pourvu que Mme Michu, son occupante, reçoive son courrier en bon état.
Par contre, il est indispensable que le courrier soit distribué dans des enveloppes à un format standard sur lesquelles figure une adresse. Sur Internet, les lettres sont des paquets, composés d’un message (Protocol Data Unit) enveloppé dans des en-têtes au format Internet Protocol, parmi lesquels figure l’adresse du destinataire. Ces en-têtes (enveloppes) sont les mêmes dans tous les AS (bureaux de poste) du monde et les adresses sont uniques. C’est ce qui fait que Mme Michu peut correspondre avec son amant chinois, M. Chang.
Chaque bureau de poste est, je l’ai dit, libre de répertorier les adresses qui en dépendent comme il veut, mais il faut que le bureau de Trifouilly-les-oies sache quoi faire des lettres destinées à M. Chang. Sur Internet, chaque AS est identifié par un numéro unique, un peu comme un code postal à l’échelle mondiale, et maintient des tables permettant de savoir de quel AS dépend chaque adresse IP. Comme la structure d’Internet est dynamique (de nouveaux AS peuvent apparaitre, d’autres disparaitre, les associations IP - AS peuvent changer…), les AS s’échangent ces informations en permanence suivant le Border Gateway Protocol, le protocole de routage dans l’Internet.
La règle fondamentale de neutralité dans tout ça est très simple à comprendre avec mon analogie : la poste (les FAI) transporte le courrier (les paquets) de son point de départ à son point d’arrivée, et elle ne fait que ça. Le facteur n’ouvre pas les lettres pour lire les déclarations enflammées de M. Chang à Mme Michu, et il ne glisse pas de publicité dans les enveloppes. Sur Internet, il n’y a pas non plus de service de type Chronopost : un AS peut passer un accord avec ses voisins pour qu’ils transportent certains paquets plus vite, mais il ne l’écrit pas dans les en-têtes IP, et même s’il le fait personne n’a l’obligation de respecter ces instructions. On dit qu’un réseau IP, et en particulier Internet, ne gère pas les règles de qualité de service (QoS) mais fait simplement de son mieux (Best Effort).
Maintenant, on peut essayer de voir ce qui n’est pas neutre, et donc pas Internet. Un réseau où les machines n’ont pas d’adresses IP publiques n’est pas Internet. Elles ont des adresses IP, certes, mais une machine sur Internet ne peut pas leur envoyer de paquets directement. Bayart donne l’exemple des opérateurs mobiles, je vais en prendre un autre : si vous êtes connecté au ResEl, vous n’êtes pas sur Internet, parce que l’adresse attribuée à votre interface réseau n’est pas accessible de l’extérieur. C’est ce qui fait que vous ne pouvez pas héberger votre serveur web, par exemple. C’est une bonne occasion d’enfoncer une porte ouverte : Internet a un coût. Je ne connais pas de vrai Fournisseur d’Accès à Internet pour 10 € par an.
Un FAI ne devrait pas non plus empêcher certains paquets de passer. Si le votre bloque le peer-to-peer, il viole le principe de neutralité. En fait, le simple fait qu’il sache que vous faites du peer-to-peer suffit : il a regardé dans l’enveloppe. Si votre FAI traite les paquets d’un fournisseur de service (disons Dailymotion) plus vite que ceux d’un autre (disons Youtube), il y a aussi de fortes chances qu’il viole la neutralité du Net, sans parler de la législation sur la concurrence. Si quand vous consultez le web un bandeau s’affiche en haut de chaque page et vous rappelle que votre connexion est fournie par Machin, vous n’êtes pas sur Internet. Si vous ne pouvez pas installer un serveur SMTP sur le port 25 de votre machine… vous avez compris l’idée.
Un point tendancieux : je considère personnellement qu’Internet se limite à ce que j’ai décrit plus haut, ce qui veut dire qu’un filtrage BGP ou sur les IP est contraire à la neutralité du Net mais un filtrage au niveau des noms de domaine (DNS) pas forcément, dans le sens où vous bannir des pages blanches n’empêche pas de vous écrire. Je ne dis pas que j’y suis favorable, juste que c’est moins grave.
Maintenant, pourquoi la neutralité du Net est-elle si importante ? Parce que c’est ce qui lui permet de garantir des droits fondamentaux tels que la liberté d’expression (c’est pas moi qui le dit, c’est le Conseil Constitutionnel) ou la libre concurrence, et aussi parce que c’est parce qu’Internet est neutre qu’il peut fonctionner, techniquement et d’un point de vue diplomatique. Simplement parce que si on commence à dire qu’Internet n’est plus neutre (ou ouvert, c’est à peu près équivalent), alors c’est qu’on en laisse le contrôle à quelqu’un, et les autres ne risquent pas d’être d’accord.
Alors évidemment, il y en a que ça tenterait bien de contrôler Internet. On l’a vu, la neutralité va plus loin que l’absence de censure, mais elle l’inclut, et c’est bien de cela qu’il est question quand on vous parle de filtrage. Bien entendu, le gouvernement et leurs amis des médias vous diront qu’ils cherchent à empêcher les méchants terroristes et leurs comparses pédophiles de nuire, parce qu’ils ne vont pas vous dire ouvertement qu’ils veulent juste reprendre le contrôle de l’information. Demandez donc à M. Chang ce qu’il en pense !
Donc non, la neutralité du Net n’est pas une obsession de libristes illuminés, et les artistes qui la comprennent n’en pensent pas ce qu’on voudrait bien nous faire croire. La France, en la matière, est en train de nager à contre-courant ; elle finira par s’épuiser et couler si elle persiste.
Pour conclure, un conseil : si vous voulez vous faire une idée sur le sujet, éteignez votre télévision, débranchez votre radio et oubliez les journaux. On ne juge pas les abus d’un état en prenant pour argent comptant sa propagande. Le seul endroit où l’on peut s’informer objectivement sur Internet en France, c’est le Net lui-même. Du moins, tant qu’il lui reste un semblant de liberté…
Ceux qui me connaissent savent que râler, je sais faire. J’essaie d’éviter autant que possible, mais parfois c’est nécessaire.
On la critique en France mais la redevance TV est bien pire en Angleterre. Si dans l’hexagone elle est perçue par l’État, ici c’est une société privée qui s’en charge. Et contrairement à chez nous, ni les gens qui regardent la télé sur Internet ou sur un téléphone mobile ni les étudiants en résidence ne sont exemptés.
Cette société, TV Licensing, envoie régulièrement des lettres de menace aux délinquants qui osent capter la bonne parole diffusée sur les ondes sans s’acquitter de la dîme. Hmm, un petit arrière-goût d’HADOPI ? J’ai reçu cinq lettres, et je suis d’après leurs dires “scheduled for visit”. Ils peuvent venir, j’ai l’esprit tranquille : je ne regarde pas la TV, la loi anglaise ne leur permet pas d’entrer chez moi et de toute façon c’est tellement difficile techniquement d’atteindre ma porte que ça m’étonnerait qu’ils y arrivent un jour.
Depuis que j’habite ici, j’ai bien dû être réveillé une vingtaine de fois par des alarmes incendies en pleine nuit. La première fois, enfiler un manteau, des chaussures et descendre devant l’entrée se geler pendant une demi-heure en attendant l’arrivée de la sécurité peut être drôle, mais à la longue ça lasse. Qui plus est, le dispositif d’alarme permet de savoir aisément quel détecteur a déclenché l’alerte, et donc de confirmer rapidement que c’est une erreur, mais la loi anglaise stipule qu’ignorer l’alarme et retourner au chaud dans son lit est un crime !
Si jamais un feu se déclenche vraiment, j’espère que les familles des victimes penseront à faire un procès à tous les responsables du dispositif d’alarme. Un système d’alerte qui génère autant de faux-positifs est totalement inutile, simplement parce que personne n’y prête plus attention. Tous les administrateurs système qui ont déjà essayé de lire des logs doivent bien le savoir…
C’est en tout cas ce que pense Mark Zuckerberg, le patron de Facebook. Il a le droit, et j’ai le droit de ne pas être d’accord. Comme mon ami Dédé, je vais donc logiquement quitter le célèbre réseau social.
De toute façon, les soi-disant réseaux sociaux sont des aberrations, des substituts au Web pour ceux qui ne l’ont pas compris. Vous voulez donner des nouvelles, ou même poster des liens débiles ? Ouvrez un blog (on appelait ça “une page perso” à l’époque révolue du Web 1.0), ou twittez si ça vous amuse ! Il semblerait que la raison qui pousse les gens à rejoindre Facebook soit la simplicité d’ouverture d’un compte. Ouvrir son blog aussi est très simple.
Pour ma part, je ne consulterai pas Facebook en février. Je ne clôturerai mon compte que début mars de sorte que les gens qui n’ont que ce moyen pour me joindre puissent m’en demander un autre par message privé (je les reçois dans ma boite mail). Si vous trouvez que je suis paranoïaque, sachez simplement que l’un des sujets proposés en MSc Thesis à Cranfield consiste à faire du data-mining (collecter plein d’informations sur vous) sur Facebook en utilisant le Grid Computing (un paquet d’ordinateurs, dont des super-calculateurs, en réseau). Si de tels moyens, habituellement réservés à de très grosses industries comme le pétrole, l’automobile ou la mode, sont déployés dans ce but, c’est simplement parce que vos données personnelles valent très cher.
Java est un langage tout pourri, si je commençais à énumérer toutes les raisons pour ça il me faudrait autant de lignes que pour coder une application de taille moyenne dans le dit langage, et oui ça fait beaucoup.
En tout cas, étant contraint et forcé d’en faire pas mal en ce moment, j’ai pu bien m’arracher les cheveux sur quelques points :
string.length(), mais table.length. La cohérence, vous connaissez ?
Avec Java, Axis et un programme un peu complexe, il suffit qu’une exception se déclenche pour afficher une trace qui fait cinq fois la hauteur de votre console. Bien entendu, le message d’erreur intéressant est en haut.
Enfin et surtout, c’est lent, à la compilation comme à l’exécution.
Voilà, tout ceux qui l’ont utilisé savent que Java c’est mal. Après, il y en a qui aiment, et je n’ai rien contre les masochistes, mais je supporte déjà moins les sadiques qui forcent des gens sains d’esprit à s’en servir.
J’ai l’impression que mon article sur les habitudes consommatrices de temps en ligne avait intéressé quelques personnes. Je vais revenir brièvement sur ce qui est arrivé depuis et en tirer un rapide bilan.
Dans l’ensemble, j’ai fait ce que j’avais prévu : je ne suis plus le forum d’archlinux.org ni Tuxmachines et j’ai arrêté les jeux Motion Twin. Ça m’arrive encore pas mal de cliquer sur des liens par pure curiosité mais j’imagine que c’est une habitude difficile à perdre.
En plus de ça, je consulte beaucoup moins Facebook pour lequel j’ai perdu de l’intérêt, j’ai encore écrémé un peu mes inscriptions aux mailing-lists, je relève ma messagerie moins fréquemment, j’ai quitté certains canaux IRC, et je me suis mis à utiliser surf beaucoup plus que Firefox, ce qui limite la tentation de faire plusieurs choses à la fois (onglets) ou de remettre des choses triviales à plus tard (marque-pages).
J’ai l’impression que ces quelques changements n’ont pas trop mal fonctionné, et j’apprécie de m’être dégagé un peu de temps pour faire des choses plus enrichissantes. Je suis aussi content de ne pas avoir eu à recourir à mes idées plus radicales. Je pense que le plus important était surtout de prendre conscience du problème et de commencer à me demander de temps en temps, avant d’ouvrir une page web : “n’ai-je pas mieux à faire ?”.
J’avais promis que j’en parlerais : comme tout le monde (ou pas), j’ai regardé les vidéos diffusées par Google sur Chrome OS et je me suis fait ma petite idée.
La première chose qui me vient à l’esprit, c’est que même Benjamin Bayart était probablement loin d’imaginer combien il était proche de la vérité lorsqu’il parlait de Minitel 2.0. Le principe de Chrome OS, c’est le retour aux clients légers, aux terminaux. Toute l’information est dans les nuages, rien sur votre machine. Il ne supporte même pas les disques durs au profit de la mémoire Flash !
Du point de vue technique, l’interface emprunte de très bonnes idées aux gestionnaires de fenêtres minimalistes : applications en plein écran, panneaux qui se superposent aux fenêtres à une position fixe… Tout le travail effectué pour démarrer plus vite est aussi intéressant et se rapproche de ce que font certains utilisateurs d’Arch Linux amateurs d’optimisation et de bootcharts.
Le problème, comme toujours avec Google, n’est pas vraiment la technique. C’est le vrai but de l’opération qui est difficile à deviner. Chrome OS est open source, ce qui est très bien, mais qui ira lire les sources, les comprendre ? Qui saura influer sur l’évolution du système ? Qui utilisera d’autres serveurs que ceux de Google pour stocker ses données ?
Et même si le système client est open source, je le vois un peu comme un piège qui va influer lentement sur la mentalité du public et lui faire considérer comme normal de ne pas avoir le contrôle de ses données. Google sait bien que dicter leurs habitudes aux gens est une stratégie efficace, c’est ce qui fait que les tortues asthmatiques que sont Internet Explorer et Outlook dominent encore largement leurs marchés respectifs. Chrome OS, c’est une drogue dont la première dose est gratuite et ne fait aucun mal, mais ensuite ?
Heureusement, il y a de l’espoir : je pense que Google s’est laissé emporter par son élan et que Chrome OS est bien trop en avance sur son temps. Pourquoi ? Il cible principalement le marché des netbooks. Or, les gens achètent des netbooks entre autres parce qu’ils sont faciles à utiliser dans les transports en commun. Et vous êtes pratiquement certain de ne pas trouver de wifi gratuit dans un train, encore moins dans un avion. Votre netbook sous Chrome OS devient donc soudainement inutile : vous ne pouvez pas regarder votre film préféré parce qu’il est resté là-haut dans les nuages, et vous ne pouvez pas travailler sur votre rapport parce que Google Docs n’est pas accessible.
Bref, nous voilà encore une fois sauvés de l’horrible Big Brother du 21e siècle. Oh, et sinon, je viens de commencer à utiliser ce formidable outil qu’est Wave ! ;)
Regarder le top 10 d’Alexa peut faire peur quand on connait un peu ce qu’est Internet. Ce classement par trafic est assez représentatif de l’évolution du web ces quelques années. On y trouve évidemment beaucoup de moteurs de recherche : google.com (1er), yahoo.com (3e), live.com (5e), baidu.com (8e) et yahoo.jp (10e). msn.com, le portail de Microsoft, arrive en 9e position, pourquoi pas. Le problème, c’est les quatre autres. Prenons-les dans le désordre.
Deux services de Google, bizarrement. Tous deux vous permettent d’héberger du contenu, vidéo pour YouTube et principalement textuel pour Blogger. Ce contenu pourra ensuite être commenté par d’autres utilisateurs, c’est ce qu’on appelle Web 2.0. Qu’y a-t-il de mal à cela ? Pas grand-chose, en fait, si ce n’est la situation de quasi-monopole de ces plates-formes.
En offrant un service simple et gratuit, Google [1] s’est imposé sur ces secteurs. Or, même si Google n’est pas maléfique, il est bon de se rappeler que c’est une entreprise dont le business model repose sur deux choses : vous vendre de la publicité et apprendre des choses sur vous. Vous lui donnez une occasion en or de faire la deuxième en vous créant un compte personnel chez eux et en y hébergeant vos données, quant à la première il n’y a qu’à voir le principal changement sur YouTube pour l’utilisateur depuis le rachat [2].
Autre problème commun à tous les services gratuits gérés par de grosses boites : vous ne valez pas grand-chose pour eux, et ils ne feront rien pour vous aider, par exemple, à éviter la censure. Il suffit de voir le nombre de vidéos supprimées de YouTube sans raison valable, sur simple demande de gens qui ne sont souvent pas les ayant-droits. Certains se souviennent peut-être de la vidéo où Mme Albanel prouvait son incompétence en annonçant qu’OpenOffice incluait un pare-feu. Cette vidéo a été censurée à de multiples reprises bien qu’elle ait été filmée de manière officielle à l’Assemblée Nationale.
Ceci dit, ces problèmes sont mineurs, il suffit d’en être informé, et ces services ont une raison d’être sur Internet. Pour les deux qui suivent, c’est une autre histoire.
[1] OK, pour Youtube c’était déjà le cas avant que Google achète…
[2] Pour ceux qui ne l’utilisent pas, je parle de publicités sous forme de bandeaux qui s’affichent en superposition sur la vidéo.
Wikipedia, l’un des plus gros succès du libre. Une source d’information à laquelle chacun peut contribuer s’il a les compétences nécessaires, accessible instantanément de tout point du monde. Une merveille !
Attendez, ça ne vous fait pas penser à quelque chose ça ? Ah si, le Web… Sauf que Wikipedia a un modèle centralisé alors que le Web est distribué. Sauf que sur Wikipedia ce sont les modérateurs qui détiennent le pouvoir de décider si une information est valable ou non, alors que sur le Web c’est à chacun de se faire son idée, même si l’opinion dominante se trouvera en premier dans les moteurs de recherche. Sauf que sur Wikipedia tout devra s’appuyer sur des sources “fiables”, comme par exemple des publications scientifiques (hum). Bref, Wikipedia, une formidable machine à imposer une pensée unique à des utilisateurs qui ne demandent pas mieux.
EDIT - Pour ceux qui ne s’en seraient pas rendu compte, j’exagère volontairement. Ne prenez pas tout pour argent comptant non plus, la diversité d’opinion existe bien sur Wikipedia dans une certaine mesure.
J’ai gardé le meilleur pour la fin. Juste derrière le roi des moteurs de recherche, le sultan des réseaux sociaux. Facebook, la toile dans la toile, mais une toile encore une fois centralisée et où on ne communique qu’avec ses “amis”. Pratique, je peux dire des bêtises comme ça, personne ne le verra. Et si jamais un jour je ne les assume plus, j’efface.
Mouais. Vous faites vraiment une confiance absolue à tous vos contacts, vous ? Si c’est le cas, bravo. Sinon, une fois que vous aurez réalisé que tout ce que vous mettez sur Facebook est du numérique, donc de l’information copiable, vous vous rendrez compte que, quels que soient les réglages de votre compte, il vaudrait mieux considérer que tout ce que vous y postez est public.
Et puis même au-delà de ça, comment comprendre qu’un tel principe ait du succès ? Parce qu’après tout, soit vous n’ajoutez pas de contenu et vous ne faites que poster des liens vers des choses que vous avez trouvées en surfant sur le Web ou partager ceux des autres, et ça ressemble fortement aux chaînes d’emails que personne ou presque n’aime, soit vous mettez des choses nouvelles en ligne et c’est souvent égoïste parce que vous réservez (théoriquement) à vos pseudo-amis l’accès à de l’information qui n’est en général pas si privée que ça et qui aurait peut-être pu servir ou faire plaisir à d’autres. Regardez un peu en arrière, même les kikoolols de la génération Caramail préféraient en général les forums publics aux forums privés.
Oui, parce qu’il faut bien le dire, il y a un problème. Et ce n’est pas que ces trucs-là existent, ils en ont le droit, c’est même un des fondements d’Internet : n’importe quel service a sa chance.
Non, le problème, c’est que bien que je voie tous leurs défauts gros comme des maisons, je les utilise ! J’ai un compte YouTube, un compte Facebook et ça m’arrive régulièrement de consulter des pages Wikipedia. Seule exception : ces quelques pages ne sont pas chez Google mais bien sur mon VPS, sous mon nom de domaine. Ouf.
La raison pour laquelle je les utilise, c’est que bien que conceptuellement et idéologiquement ils soient percés de partout, techniquement, ils vont de pas trop mauvais (Facebook, Wikipedia) à très bons (tout ce que fait Google). C’est un peu la différence avec Windows ou Internet Explorer, vous voyez : le fait qu’ils sont techniquement mauvais fait que leurs alternatives sont crédibles, pas vraiment la question éthique qui est derrière.
Conclusion ? Rien. Que voulez-vous que je dise de plus ? Voir que ces sites-là sont parmi les plus visités du monde me rend un peu triste mais ce n’est pas pour autant que je vais arrêter de les utiliser. Pas tout de suite en tout cas.
Peut-être qu’un jour j’aurai le temps de mettre en place un système simple pour héberger mes vidéos sur mon serveur. Peut-être que je me lasserai de Facebook (les gens que dont je voudrais avoir des nouvelles n’y postent plus trop de toute façon) et que je fermerai mon compte. Peut-être qu’on reverra des annuaires collaboratifs de liens vers des pages qui donnent des avis variés sur divers sujets, avec une structure d’arbre ou de graphe par thématique et un hébergement décentralisé, de la réplication spontanée, du Web quoi… Houlà, je m’envole ;)
En attendant le miracle, je profite de la qualité de ces services, comme tout le monde. Je ne suis pas assez fanatique pour les boycotter. J’essaie juste de me rappeler de temps en temps qu’on est bien loin de l’utopie du Web de 90.
La prochaine fois, je parlerai peut-être de Chrome OS, un sujet pas si éloigné finalement.
Hier soir, j’ai joué à Featherweight, un bon petit jeu d’aventure indépendant qui se finit en quelques minutes. J’ai beaucoup apprécié, et ça m’a fait réaliser que ça faisait longtemps que je n’avais pas fait quelque chose du genre. La raison ? Je suis atteint d’un syndrome qui touche beaucoup de gens de ma génération : je passe la plupart de mon temps libre à faire des choses inintéressantes sur mon PC. Du coup, je ne fais plus un certain nombre de choses que je faisais avant et qui me plaisaient beaucoup, comme jouer à des jeux vidéos ou coder pour moi, et ça me manque. C’est grave docteur, ça se soigne ? À mon avis oui, mais il va falloir reprendre les choses en main et supprimer un certain nombre de distractions.
Heureusement, le principal danger est évité : je gère à peu près bien ma messagerie instantanée. Je n’ai jamais utilisé de réseau type MSN, et je vais beaucoup moins sur IRC depuis que phrakture me l’a conseillé. J’ai ajouté un peu trop de contacts dans Skype à mon goût, mais ça reste gérable. Je me suis aussi désinscrit de la plupart des mailing-lists de Télécom qui me prenaient pas mal de temps à lire, et j’ai délaissé identi.ca (un twitter-like libre). Par contre, je fais un paquet de choses inutiles par habitude, que je vais arrêter immédiatement.
D’abord, lire les nouveaux messages sur le forum anglophone d’Arch Linux. Il y en a beaucoup trop maintenant, plusieurs centaines par jour, ça n’est plus gérable. Je continuerai à lire ceux du forum francophone de temps en temps pour l’instant.
Ensuite, jouer à des jeux en ligne, du genre de ceux publiés par Motion Twin. Jouer à des jeux vidéos n’est pas forcément mauvais, c’est jouer à des jeux sans fin le problème (MMORPG, jeux Flash, etc). Je vais faire une exception de Shinobi pour l’instant parce que je m’y suis pas mal impliqué et qu’il ne me prend pas beaucoup de temps.
Enfin, j’arrêterai de suivre Tuxmachines ainsi que les liens lorsque je lis un article ou, pire, les suggestions de Youtube quand je regarde une vidéo.
J’évaluerai l’efficacité de ces mesures dans environ deux semaines. Si ça ne suffit pas, j’en prendrai d’autres.
Je commencerai par supprimer de mes contacts Facebook tous les gens qui ne sont pas des amis proches, surtout ceux qui spamment avec des quiz débiles (SVP, stop).
Je supprimerai des RSS de mon lecteur de news. Certes, le RSS, c’est du push, donc bon pour la productivité, mais trop de webcomic tue le webcomic, et pareil pour l’actualité technologique.
J’arrêterai Shinobi, le forum archlinux.fr et je me désinscrirai de presque toutes les mailing-lists auxquelles je suis abonné.
C’est un peu violent, certes, mais je pense que j’y gagnerai beaucoup. Et si jamais tout ça ne suffit pas, j’ai bien encore des idées comme utiliser un navigateur, un client mail et un lecteur de flux RSS en mode texte seulement, mais j’espère bien ne pas en arriver à de telles extrémités, pourtant courantes chez les gens adeptes du worse is better. Je n’irai quand même pas jusqu’à faire comme Knuth, qui n’utilise pas l’email…
J’espère en tout cas que tout ça me permettra de gagner en productivité et en temps libre pour faire des choses utiles et/ou qui me plaisent plus.