347 private links
For instance, in Go, there is a context.Context value, which manages timeouts, cancellation, and a small amount of data that can be threaded through various functions. It is, by design, intended to be something passed through functions even if the receiver doesn’t use it directly but only passes it along.
Rust, Haskell, all programming languages I know have colored functions.
For a “normal” change, it only propagates to the caller. It may then chain to its caller. If it propagates all the way up to the top-level function, as demonstrated in the diagram, this may seem like a distinction without a difference.
I think with some current changes to Zig a function that does IO either is or is not a color depending on which Io value you pass to it, or perhaps rather, the color is an attribute of the Io value rather than the function itself.
Rust, Haskell and all programming languages supporting async. Async is assumed to be a color because it forces to change all functions using an async function.
a color must have this characteristic where it jumps over intervening calls and forces the change on all other functions within the relevant island of color.
See the related post https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/
In this post, I’ll walk through a set of common misconceptions that drive teams to introduce new infrastructure when they don’t need to. All of these can be solved with vanilla PostgreSQL 18 using standard extensions available on RDS, with no special infrastructure and no distributed-systems cosplay.
The database can be used for many things people often miss:
- user roles (finance) for modularity. In particular, a proper role can be used for defining very precise interfaces on top of database objects
- domains can be defined as an application of the abtract data type to database management
- a table for transfers can be stored with a system-time temporal table
- account audits (store old versions for logs)
- transaction state machine with functions
- record size estimation
- Heap-Only Tuple to avoid write amplification when primary keys are aligned with immutable columns. It enables faster updates. See https://ebellani.github.io/blog/2026/all-you-need-is-postgresql/#introduction:~:text=Enabling%20HOT%20Updates%20for%20Transfers,-By
- excellent OLTP (quick read and write individual rows) with a primary key reflecting the access pattern
- view to reuse queries from multiple tables
- excellent OLAP: large volumes of data while allowing quick query response times: a
finance.balance_ledgertable stores a snapshot of both balances after every transaction. - incremental maintenance via triggers
- correct isolation with
alter role finance set default_transaction_isolation = 'serializable';. If a cyclic transaction is detected, then one of the transaction fails. The application must be prepared to retry aborted transactions, but the invariants are never violated.
Key takeaways:
- 766 TPS overall with 0 failed transactions. The startup target of 10,000 transfers/day is ~0.12 TPS. We exceed that by over 6,000x, confirming massive headroom on modest hardware.
- 0 serialization failures: Despite 10 concurrent clients writing to 100 accounts under SERIALIZABLE isolation, no transactions were aborted. The working set is distributed across enough accounts that write contention is negligible at this scale.
- 8ms average read latency: The UNION ALL view over 4 tables plus the balance ledger lookup completes well within interactive response time, even as the tables grow throughout the 60-second run.
- 33ms average write latency: Each write transaction exercises the full constraint set that implements complex business logic (audit trails, time constraints, balances, etc) all within 33ms. This is the true cost of enforcing every business rule at the data level, and it is more than acceptable.
The full SQL is provided at the end.
Using every data from RAM and your application needs to be specifically designed to never write to the filesystem and instead stream data directly to object storage.
but it has some benefits.
A simple strategy to build subproceses that does one thing and one thing well.
How to architecture a website built before deployment. The pros and cons. How to build the content with Next.JS
Note it was a talk in 2025/2016
RFC -> Review -> Decision meeting -> ADR
# the api key generation
fn hash_api_key(api_key_id: Uuid, version i16, organization_id: Uuid, secret: &[u8]) -> [u8; API_KEY_HASH_SIZE] {
let mut hasher = sha3::Sha3_512::new();
hasher.write(api_key_id.as_bytes());
hasher.write(&version.to_le_bytes());
hasher.write(organization_id.as_bytes());
hasher.write(secret);
return hasher.sum();
}
the storage in the database
CREATE TABLE api_keys (
id UUID PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE NOT NULL,
name TEXT NOT NULL,
expires_at TIMESTAMP WITH TIME ZONE,
version SMALLINT NOT NULL,
secret_hash BYTEA NOT NULL,
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
UNIQUE (name, organization_id)
);
CREATE INDEX index_api_keys_on_organization_id_and_expires_at ON api_keys (organization_id, expires_at);temporary flags are rarely temporary. Once a flag exists, it starts attracting dependencies.
Documentation has to mention it. Support has to ask whether it is enabled. Bug reports have to include it. Tests need to cover both states.
A flag is a boolean and the boolean in the interface usually means a branching factor in maintenance.
The solution:
Every new flag should come with an expiration story.
Why does it exist? Who needs it? What breaks if it goes away? When will that be acceptable? If nobody can answer these questions, the flag is probably not a feature.
Exception is indeed a bad programming ergonomic that can lead to many failures
Fewer moving parts means you move faster. More importantly, you can make architectural changes faster. When your infrastructure is basically “one Postgres instance,” new developers can get the full stack running on their laptop in minutes, not days.
JSONB columns for schemaless key-value store.
Job queues with a SQL table running SELECT ... FOR UPDATE SKIP LOCKED
Full-text search built-in
Yes, you should use the best tool for each job, but every tool is another thing that can break, to deploy, to hires need to learn and to keep up.
When to not use Postgres: the queries starts taking 100s milliseconds and the indexing is exhausted. Once this happen, you'll have a clear spec
An example of entry point in a documentation
- A simple queue.json
- Batching with group commit
- Use a brokered group commit to eliminate contention over the queue object
- HA brokered group commit to handle unfinished job or broker machine die
Similarly to the job system we built at work, it guarantees at-least-once delivery.
I don't know if the pattern becomes too complex to be viable.
The ambiguity level and the number of unknowns are definitely a crucial factor when it comes defining our modules, and especially when it comes to the implementation strategy choice
The more ambiguity we have, the more fluid and dynamic our domain is and the less certainty about its final shape we have, the more we should focus on adopting a strategy where it is the least costly to completely redesign and rearrange our modules.
Simple modular monolith: folders. Microservices needs one application per module. That's a high cost.
requirements, and some of them might have needs somewhere in between. Thanks to the fact that every module is now basically a separate application, we can assign different resources to each module and have it in a different, often dynamic, number of replicas, based on its own unique needs.
We can eliminate many problems of microservices by adhering to one, simple rule:
When serving any external network request, synchronous or asynchronous, a service can not make any network calls to other services, synchronous or asynchronous.
About SPAs: avoid global things that apply everyhwere
As the last resort, we can have a separate SPA per a few selected routes, having as many html pages as we have SPAs (multiple SPAs approach), or use the Micro Frontends
To reiterate, we went through the following strategies, ordered from simplest to the most complex one:
- Simple Modular Monolith
- Modular Monolith with Isolated and Independently Deployable Modules
- Modular Monolith with Helper Services
- Constrained Microservices - Microliths
- Microservices
I would say things are always easy when the modules are clear, defined and documented :)
1. Event-Driven Architecture (EDA)
Problèmes résolu:
- timeout si un service est lent
- 1 service down = toute la chaîne bloquée
- temps de réponse imprévisible
Pièges à éviter:
- Event explosion
- Debugging de l'enfer
- Eventual Consistency mal gérée
- Cohérence transactionnelle
2. API-First & API Gateway pattern
API-First : Concevoir l'API avant d'implémenter le service
API Gateway : Point d'entrée unique qui orchestre, sécurisé, et monitore les APIs (et Backend for Frontend)
Pièges à éviter:
- moins de 5 api et un seul frontend
- communication interne uniquement
- latence critique
3. CQRS + Event Sourcing
Command Query Responsibility Segregation: séparer les modèles de lectures et d'écritures; deux bases de données différentes optimisées pour leur usage.
Event Sourcing : Au lieu de stocker l'état actuel, on stocke tous les événements L'état actuel est reconstruit en rejouant les événements.
Cas d'usage: Performance, audit et compliance, analytics temps réel
Pièges à éviter: complexitée surévaluée, eventual consistency, gestion de la mimgration de schéma
4. Saga Pattern
Here's the thing: 99% of companies don't need them. The top 1% have tens of millions of users and a large engineering team to match.
The fun thing about Postgres is there is already an extension for that: PostGIS, Full-text search, JSONB, TimescaleDB, pgvectorm, and many for AI
Each database add hidden costs: backup strategy, monitoring dashboards, seceurity patches, on-call runbooks, failover testing.
SLA math: Three systems at 99.9% uptime each = 99.7% combined
Why? Costs, operational complexity, data consistency
- Caching with UNLOGGED table (that I've also found in other posts)
- Pub/Sub with LISTEN/NOTIFY
- Job Queues with SKIP LOCKED
- Rate Limiting is also possible
- Sessions with JSONB
PostgresSQL ist nearly 2 times slower compared to Redis, but is it worth it?
When to keep Redis?
- extreme performance needed
- using redis-specific data structures
Migration strategy:
- Side by side
- Read from Postgres
- Write to Postgres only
- Remove Redis
and Prisma provide for example typedSQL to have a smooth integration with Typescript.
When messages carry their routing accross nodes, the pattern can be useful
The permission system should handle folders and files.
Strategies:
- (naive) read-time permission queries
- A simple table (RBAC role based access control).
-- RBAC: Pre-computed permissions
-- access_type: 'owner' (full control), 'shared' (read only), 'path_only' (visible but no access)
CREATE TABLE permissions (
user_id INTEGER NOT NULL,
resource_id INTEGER NOT NULL,
access_type TEXT NOT NULL,
PRIMARY KEY (user_id, resource_id),
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (resource_id) REFERENCES resources(id)
);
- Attribute-Based Access Control
This approach is very clear and composable. It works great for single-resource access checks: "can user X access resource Y?" It struggles when listing resources, as we would need to execute the policies for each resource and can't directly query the resources table with simple filters.
- Zanzibar and ReBAC