from/prod
← All companies

THE COMPANY INDEX TRACKED BLOG

Daniel Doubrovkine (dB.)

Ideas, decisions, and lessons from the team.

code.dblock.org (opens on the source site)
32Posts tracked
2 days agoLatest publication
1.9Posts / month over the last 12 months

Latest writing

20 of 32 posts

A 1998 PDF-to-Text Converter (opens on the source site)

In 1998 I needed to convert PDF files to text for a search engine called Alkaline, without requiring separately installed libraries. So I wrote a C++ parser. Alkaline is another story. I recovered the source from a backup dated December 26, 1998. It reads PDF 1.0 through 1.2, follows object references and page trees, decompresses content with bundled zlib, and prints metadata, page text and bookmark titles. There are my own string, vector and hash-table libraries in there too. The encryption code is a stub. This was a work in progress. I wasn’t reverse engineering an undocumented format.…

Read at the source

Universal Copy System Link for 97 Windows PCs (opens on the source site)

In 1997 I wrote UcsLink.Fusion for the University of Geneva. We had hundreds of PCs across the city, Sun and Windows NT servers, and Windows 95 and Linux workstations. Getting a user’s software and settings onto whichever PC they sat down at was a job for login scripts. I wrote a Delphi 2.0 application to do it instead. UCS stood for Universal Copy System. Fusion read a script that mapped network drives, copied shared software, created shortcuts, connected printers, set environment variables and synchronized the clock. It could include other scripts, test conditions and clean up files left by…

Read at the source

File & Folder 98: a 1998 Word Document Manager (opens on the source site)

In the 1990s I wrote a document manager for a small business in Geneva with about 20 workstations. Most of its staff were new to computers, and folders were a challenging idea to grasp. They saved Word documents wherever Word happened to put them, and the network supervisor spent his days looking for lost files by panicked coworkers. File & Folder solved that problem. My 1999 product page said it was for “a small business which is sick and tired of seeing it’s workers putting documents a bit everywhere in the system.” The first version, File & Folder 1.0, was a Visual Basic app for Windows…

Read at the source

zBalls: a 1998 Java Applet (opens on the source site)

In January 1998, a classmate and I wrote a game. It was called “Bolongas zBalls”, and it was a Java applet. You click to start, then move the mouse, without clicking. Touch the dark ball to blow it up, and the next ball turns dark. Clear them all and the next level doubles the number of balls. Touch a plain ball and it spawns another one, so if you’re sloppy, the balls multiply until you lose. Each level has a time limit. The intro music is the theme song from Capitaine Flam, the French version of the Japanese cartoon Captain Future, which aired on TF1 in 1981. I honestly don’t remember why…

Read at the source

Good Morning, Dave: the 1998 INET Conference Kiosk Launcher (opens on the source site)

In July 1998, the Internet Society held INET ‘98, “The Internet Summit”, at Palexpo in Geneva. The University of Geneva provided about 250 computers for the conference, and I was a student there. I wrote much of the conference network’s website, www.inet98.ch, and the launcher that ran on the public Windows 95 PCs instead of Explorer. The Inet 98 Launcher was a full-height bar on the left of the screen with a button for each installed application: Internet Explorer, Netscape, PC Pine, Telnet, WS-FTP and Office 95 and 97. I wrote it in Delphi 3. It showed a screen saver when a machine sat…

Read at the source

The Second Life of Autoconf for MS-DOS (opens on the source site)

Before GNU Autoconf generated configure scripts, there was a DOS program called Autoconf. It let you keep one CONFIG.SYS and one AUTOEXEC.BAT, then choose among boot configurations by pressing a key. You did not have to wait for a menu. The PC BIOS kept early keystrokes in its keyboard queue, so you could press a configuration letter before AUTOCONF.SYS had even loaded. When the driver finally ran, it found the key and continued immediately. I did not write the original. I copied its x86 assembly source by hand from a French computer magazine. It was the first assembly program I had ever seen…

Read at the source

Porting My 1996 Pascal/Delphi Calculator to Rust (CLI + MCP) (opens on the source site)

In his Rails World 2026 keynote DHH talks about one-shot apps built almost instantly with an agent as part of Omarchy. His canonical example is Omacalc, a dead simple calculator. It’s a good demo and not a hard problem. Let’s take it further and rewrite a more advanced calculator in Rust that we can also run as an MCP server, so an AI coding agent can outsource arithmetic to it instead of hallucinating a square root. In 1994 I wrote an expression evaluator for a lab assignment. It grew into Expression Calculator, a shareware Windows app I sold through a company I co-founded, Vestris Inc. In…

Read at the source

Using AI Effectively in Technical Writing (opens on the source site)

I write a lot, and I use AI a lot. But AI does not write my blog posts. Mostly. AI is useful for making some of my writing less tedious. Recently, I asked GitHub Copilot CLI to read a few dozen posts from this blog and derive a VOICE.md to assist my writing. The result is now checked into this repo and linked from AGENTS.md, so any AI agent working here gets the same reminder: this blog is authored by me, not by AI and the agent’s job is to help me rather than pretend to be me. The useful and interesting part of VOICE.md was how it captured my weirdly specific patterns. Start with a real…

Read at the source

Serving Markdown for AI Agents, Now as a Jekyll Plugin (opens on the source site)

Back in January I wrote Serving Markdown for AI Agents: for every page on this blog, there’s also a .md version at the same URL, discoverable via a tag, so AI agents can fetch clean Markdown instead of parsing HTML. It also made me $360, via referral conversions an AI agent apparently generated after reading the clean Markdown version of a post. That worked, but it lived in this repo as a one-off script, _scripts/render_markdown.rb, wired into the GitHub Actions deploy workflow (needed since GitHub Pages’ native build only allows a small allowlist of plugins, which jekyll-md isn’t on), with…

Read at the source

In Meetings, Raise Hands Before Reading (opens on the source site)

I spent five and a half years at AWS before moving (back) to Microsoft, where I work today. At Amazon, many meetings began with silent reading. Instead of presenting a document, the author gave everyone time to read it before the discussion started. There was a small facilitation trick that made this work surprisingly well: when reading began, everyone raised a hand. When someone finished, they lowered it. The meeting started with all hands up and counted down to zero. At Microsoft, I found the inverse practice: everyone began with their hands down and raised one when they finished. At the…

Read at the source

Porting a Discord Bot to Microsoft Teams, Entirely With AI (opens on the source site)

In 2023 I ported slack-strava to Discord by hand, evening by evening over about three weeks, and it took most of that stretch to get a working bot. This week I ported the same app to Microsoft Teams as teams-strava (“Strata”), and this time I wrote almost none of the code myself. GitHub Copilot CLI did. The 2023 Baseline discord-strava’s git history is a fair record of how long a manual port takes. The first commit, “Initial fork from slack-strava,” landed July 30, 2023. “Most interactions working” was three days later. Getting install/help/error-handling polish took through August 20:…

Read at the source

Standardizing Exception Message Style in Ruby (opens on the source site)

While reviewing dozens of PRs from ericproulx optimizing Grape’s internals recently, I noticed that the bare raise ArgumentError, "..." calls scattered across the codebase were inconsistent: some messages were capitalized, some ended in a period, most were not. Longtime readers know where this is going. Ruby’s own core and standard library exceptions don’t do this - TypeError: no implicit conversion from nil to integer, ArgumentError: wrong number of arguments, and so on all read lowercase and unpunctuated, because the message is meant to be read after the exception class name and a colon,…

Read at the source

Adventures in Daylight Saving, Norfolk Island, and Time Zone Math (in Ruby) (opens on the source site)

distance_of_time_in_words is a small Ruby gem that turns two Time objects into a human-readable string like “3 days and 4 hours”. Two separate bug reports against it this year turned out to be variations on the same theme: computing a duration between two timestamps is not the trivial subtraction it looks like, the moment time zones are involved. Both fixes shipped in dotiw 5.6.0. Bug 1: dst? Lies When You Least Expect It #63 reported that a duration of one minute was rendered as “less than 1 second” for users in Europe/Dublin. The gem’s TimeHash had a DST correction that looked reasonable: d…

Read at the source

Benchmarks Are Free Now (opens on the source site)

My previous post walked through four bugs in ruby-enum, a gem I maintain, all stemming from the fact that class-level instance variables aren’t inherited by subclasses. The third fix, #59, made keys, key?, value?, key, value, to_h, parse and each walk up superclass and merge in a parent’s enums, so a subclass would see everything its ancestors defined. It was correct, fully tested, and shipped. It also made every one of those methods roughly 5x slower on any subclass. def _enum_hash if superclass Ruby::Enum superclass.send(:_enum_hash).merge(_own_enum_hash) else _own_enum_hash end end This…

Read at the source

Ruby Instance Variables Are Not Inherited (and Why That Breaks Your DSL) (opens on the source site)

The ruby-enum gem is a small library I maintain that adds enum-like behavior to a class via include Ruby::Enum and define :KEY, value. Four pull requests landed against it recently, each fixing a different symptom, and all four turned out to be the same underlying bug: class-level instance variables set in a module’s included hook are not inherited by subclasses the way you might expect. All of these fixes shipped in ruby-enum 1.2.0. Ruby::Enum stores its keys and values in instance variables on the class itself, set up when the module is included. def self.included(base) base.extend…

Read at the source

Generating AI Descriptions of Automated Pull Requests (opens on the source site)

The slack-ruby-client library, an open source Ruby gem I maintain, runs a scheduled GitHub Actions workflow that regenerates code from Slack’s API definitions and opens a pull request with the diff. The commit message and CHANGELOG entry used to be a generic “Update API (2026-08-11)”, which told a reviewer nothing about what actually changed. Here’s how we taught the workflow to describe its own diffs, using GitHub Copilot CLI, which open source maintainers can get for free. The Idea The workflow already computes a diff before opening the pull request. Instead of a boilerplate commit message,…

Read at the source

Coding with AI Agents is Now a Baseline Expectation for Managers (opens on the source site)

AI-first Engineering is now the operating default in many organizations, including mine. Developers have kept up. Vibe coding became agentic engineering, and the race towards truly autonomous engineering isn’t slowing. But what are we, managers, to do? A manager job continues to include recruiting and retaining people, setting clear goals and expectations, fostering communication, owning team workflows, and coaching daily behaviors. To this list we must now add driving AI transformation. Being a passive advocate is not enough. Unlike other subjects, no amount of AI training will help you keep…

Read at the source

How I Made $360 by Serving Markdown to A.I. agents from My Jekyll Blog (opens on the source site)

In April 2025 I wrote Apologizing for My Obsessiveness Over Punctuation, a post about my various organizational compulsions. Near the end, almost as an aside, I mentioned that I obsessively categorize expenses in Monarch, a personal finance app, linking it with my referral code. That post earned me exactly one referral in the nine months that followed. Then, on January 15th, 2026, I wrote Serving Markdown for AI Agents. The idea is simple: for every post on this blog, there’s now a .md version at the same URL. AI agents can discover and fetch clean markdown instead of parsing HTML. I didn’t…

Read at the source

Opening Proprietary Code to Contributors Checklist (opens on the source site)

Before AI coding assistants, a typical engineering team built expertise with the years: new team members joined, contributed small bug fixes, then were given more ambitious tasks over time as they became more comfortable in a codebase, to ultimately become experts. This process took years. In contrast, today, almost every engineer, and many non-engineers, have access to tools that can gather context from any codebase, and produce a large volume of high-quality code in a day. This lowers the bar for contributing meaningful changes to the software we build and enables many more individuals…

Read at the source

AI Slop: A Slack API Rate Limiting Disaster (opens on the source site)

Yesterday I described AI-generated code as “plausible-looking, locally coherent, globally wrong.” Here’s a concrete example from my own codebase. I needed a cleanup job to close old Slack group DM conversations in my slack-sup2 app. The AI-generated solution looked perfectly reasonable: def close_old_sups! return 0 unless sup_close old_sups = sups.where(conditions) old_sups.each(&:close!) old_sups.count end def close! return unless conversation_id return if closed_at logger.info "Closing DM channel #{conversation_id}..." slack_client.conversations_close(channel: conversation_id)…

Read at the source

Privacy choices

Reading never requires analytics. These choices last 90 days on this browser.

Essential sign-in and security storage always stays on. Read the privacy notice.