Skip to content
View marvelbark2's full-sized avatar

    Block or report marvelbark2

    Block user

    Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

    You must be logged in to block users.

    Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. Markdown is supported. This note will only be visible to you.
    Report abuse

    Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

    Report abuse
    marvelbark2/README.md

    Youness Masaoudi

    Senior Software Engineer / Tech Lead | Paris, France

    LinkedInEmail

    I spend my working hours on production TypeScript and Go, and my remaining
    hours writing a programming language in Rust. Both make me better at the other.


    Table of contents


    Ryo, a web-first language in Rust

    PHP won the early web for one reason that had nothing to do with language design: it knew it was serving HTTP. Everything since has been a runtime bolted onto a general-purpose language. Ryo asks what you get if the compiler itself understands routes, shared state, background work and the database.

    It started as a tree-walking AST interpreter that ran fib(40) in 81 seconds. That version is gone. So is the bytecode VM that replaced it. The current runtime compiles to bytecode and then to native code through Cranelift.

    Benchmarks

    Measured on the TechEmpower Framework Benchmarks harness.

    PathRyoReference
    Store query (JIT hot path)~675k req/s-
    Shared counter, parallel endpoint189k req/sBun: 152k req/s
    /db single query93k req/suWebSockets.js: 89k req/s
    Peak memory, large payload15 MBDown from 650 MB

    The Bun comparison is the one I find most interesting, because Ryo does not win by a large margin on median throughput. It wins at p95 and above, which is what you actually feel in production.

    Runtime architecture
    • Compilation. Source to bytecode, then Cranelift IR, then native code. Both JIT and AOT backends. Binary inspection on Mach-O arm64 showed __text dominated by cranelift-codegen at around 5 MB, with a transitive wasmtime-internal-core dependency I am still working to drop.
    • HTTP. Hyper on Tokio, with SO_REUSEPORT per core so the kernel distributes accepts rather than a single acceptor thread fanning out. Thread-per-core, moving toward io_uring via Monoio after 1.0.
    • Optimisation passes. Scalar Replacement of Aggregates at the Cranelift IR level removed roughly 13 million runtime method calls from a single bytes.at hot loop. Arena allocators and prebuilt response caches are in; kernel-level profiling with flamegraphs drives the rest.
    • Memory.Heap::Bytes(Arc<[u8]>, Range<usize>) gives zero-copy slicing. The 650 MB to 15 MB reduction came from fixing a Weak pointer cycle that was keeping whole buffers alive. The Bytes API is built around next_line and next_field returning [start, end, next], backed by memchr.
    Language design

    The primitives are chosen around what web applications actually need, not around what is easy to compile.

    • store {} is an actor with a query / action split. It solves mutable shared state without locks in user code, and it turns out to be structurally the same thing as an Erlang GenServer, which I only realised after building it.
    • parallel {} has static and dynamic forms, so the scheduler can plan the static case at compile time.
    • schedule {} puts cron-shaped work in the language rather than in a sidecar process.
    • model {} is the ORM, with the schema in the type system.
    • html {} is a first-class type that is XSS-safe by construction. You cannot concatenate a string into it.
    • memo fn and stream fn with emit from cover caching and SSE.
    • WebSocket routes are declared like HTTP routes, alongside SSE routes with heartbeat and typed events.

    Tooling ships with the language: a full LSP (diagnostics, completion, hover, go-to-definition, references, symbols, signature help, formatting), a VS Code extension, test blocks and fixture declarations inside .ryo files, and a fuzzing harness.

    The grammar is formally specified in EBNF at spec/grammar/ryo.ebnf. Individual subsystems have their own written specs before implementation: lifecycle events, config and services (config desugars to resource, with a Secret<str> type), the mail service (send versus enqueue, memory transport for tests), security defaults (CSRF on by default, rate limiting with trusted_proxies) and a cache layer with a flat normalised store and O(1) read and edit guarantees behind sharded read-write locks.

    What is not done

    The core language is feature-complete and core HTTP hardening is in place: body limits, per-request timeouts, graceful shutdown, CORS, CSRF, rate limiting, connection caps, security headers. What is still missing before I would put it in front of real traffic: database pool backpressure, full observability defaults, supply-chain signing, documentation, and any stability guarantee at all.

    The open items are tracked in roadmap/before_prod.md and summarised in spec/caution_prod.md, both in the repository. Ryo is not ready for your production traffic and I will keep saying so until it is.

    -> marvelbark2/ryo-lang


    Production work

    Technical lead on a multi-tenant SaaS platform for sports event management. I am the principal engineer across every repository in the organisation.

    Payments processed3.8M EUR / year, 9M EUR since 2022
    Registrations190 000 over 12 months
    Share of all-time volume in those 12 months74%
    Organisers on the platform600+
    PostgreSQL models under Prisma84

    That 74% number is the one that matters. Three quarters of everything the platform has ever processed arrived in the last year, and it was absorbed without a rewrite.

    Change data capture: LISTEN/NOTIFY to Debezium and Kafka

    Postgres LISTEN/NOTIFY is a lovely primitive right up to the point where you need durability, replay, or more than one consumer group. Migrated to Debezium reading the write-ahead log into Kafka, with Go consumers fanning out to ClickHouse for analytics and Meilisearch for search. Search latency on participant lookup dropped 40%, partly from the index being fed properly and partly from query rewriting on the read side.

    A WebSocket gateway for identity verification over 3G

    Race sites are fields. The network is one bar of 3G and a lot of hope. Built a Go WebSocket gateway that streams identity documents and face captures to OCR and facial recognition, chunked and backpressured for links that drop constantly. The naive HTTP upload version failed often enough to be unusable.

    Payments across three gateways

    Monetico, Fintecture and PayZen, each with its own opinion about HMAC signatures and webhook semantics. The interesting work is not the happy path, it is reconciliation: what you do when the webhook arrives twice, arrives late, or never arrives and the money moved anyway. Plus refunds, which every gateway models differently.

    Caching in three tiers

    Redis for shared hot state, Badger and Pogreb for embedded local tiers where a network hop was not worth paying for. Chosen per access pattern rather than by putting Redis in front of everything and hoping.


    Performance engineering

    The habit that connects everything above: measure at the level below the one you think the problem is on.

    1 Billion Row Challenge, Rust and Java

    Implemented independently in both, because the two languages fail in different places.

    Java. SWAR temperature parsing, branchless throughout, flat open-addressed hash table, GraalVM native image to skip warmup.

    Rust.read_unaligned for the parsing hot path, name addresses stored as offsets into the mapped file rather than as pointers or owned strings, and a triple-cursor hot loop to give the CPU three independent dependency chains to work on at once.

    Also profiled MmapSlice against a straight Vec copy on a 13 GB input, which is where the zero-copy Bytes design in Ryo came from.

    Tooling I built to do this
    • flamegraph_breakdown.py, a per-worker function-level breakdown of flamegraph output, because aggregate flamegraphs lie when your workers are not symmetric.
    • A Streamlit and Plotly benchmarking dashboard that ingests parquet and CSV so framework comparisons are reproducible rather than remembered.

    Open source

    ProjectContribution
    fullcalendar-vuePorted the component from Vue 2 to Vue 3. Merged to the vue3 branch and released as the @fullcalendar/vue3 package.
    oven-sh/bunImplemented createReadStream on Bun's native Readable API, benchmarked ahead of the Node.js path. Not merged, the feature landed independently before review.
    ryo-jsFilesystem-routed TypeScript fullstack framework. APIs, WebSockets, GraphQL, SSE, Preact components, SPA routing. The ancestor of Ryo.
    postgrest-javaJava client for PostgREST.

    Homelab

    Built from parts: MACHINIST PR9 X99 board, Xeon E5-2699 v4, Ubuntu Server, Docker, Nextcloud, Jellyfin. It has taught me more about operations than any managed platform has.

    The best lesson came from a thermal emergency. The NVMe drive hit 100 C and I could not find a workload to blame. The cause was the nouveau GPU driver flooding the kernel log at 49 000 messages per second, and the disk was cooking itself writing them down. Nothing in the application layer would ever have shown me that.


    How I work

    • Read the layer below. Most performance mysteries resolve one abstraction lower than where they appear. Flamegraphs, perf, disassembly, kernel logs.
    • Throw away the first version. Ryo's interpreter, its bytecode VM, and its early Bytes API all got deleted. Keeping a bad design because it took a long time to write is the expensive mistake.
    • Benchmarks or it did not happen. A claimed speedup without a reproducible harness is a guess with good posture.
    • Write the spec first for anything with a surface area. Every Ryo subsystem has a written specification before it has an implementation. It is faster, not slower.
    • Say what is broken. See the "what is not done" section above.

    Stack

    SystemsRustGoCJava

    WebTypeScriptNode.jsReactNext.jsGraphQL

    DataPostgreSQLRedisKafkaClickHouseMeilisearchPrisma

    InfrastructureDockerLinuxAWSNginx


    GitHub stats

    Pinned Loading

    1. ryo-jsryo-jsPublic

      Js fullstack framework, Incredibly fast

      TypeScript 5 1

    2. spring-micro-servicesspring-micro-servicesPublic

      Java 2

    3. fullcalendar-vuefullcalendar-vuePublic

      Forked from fullcalendar/fullcalendar-vue

      An official Vue component for FullCalendar

      JavaScript 2

    4. java-networkjava-networkPublic

      Java