# Daniel Doubrovkine

Published articles for Daniel Doubrovkine.

This is one page of public article previews, not the complete archive. Follow Next page to continue. Summaries are not the original full articles.

## Porting a Discord Bot to Microsoft Teams with GitHub Copilot CLI

DevFeed: [Porting a Discord Bot to Microsoft Teams with GitHub Copilot CLI](<https://devfeed.tech/articles/porting-a-discord-bot-to-microsoft-teams-entirely-with-ai-31471.md>)

Original publisher: [Read original article](<https://code.dblock.org/2026/09/16/porting-strata-to-microsoft-teams-entirely-with-ai.html>)

Author: Daniel Doubrovkine (dblock@dblock.org)

Published: 2026-09-16T00:00:00Z

Content type: article

Language: en

Sources: [Daniel Doubrovkine](<https://devfeed.tech/sources/daniel-doubrovkine.md>)

Topics: [GitHub Copilot CLI](<https://devfeed.tech/topics/github-copilot-cli.md>), [Microsoft Teams](<https://devfeed.tech/topics/microsoft-teams.md>), [Artificial Intelligence](<https://devfeed.tech/topics/ai.md>), [Discord](<https://devfeed.tech/topics/discord.md>), [AdventureX 2025](<https://devfeed.tech/topics/adventurex2025.md>)

Tags: [ai](<https://devfeed.tech/tags/ai.md>), [discord](<https://devfeed.tech/tags/discord.md>), [github-copilot-cli](<https://devfeed.tech/tags/github-copilot-cli.md>), [hackathon](<https://devfeed.tech/tags/hackathon.md>), [microsoft-teams](<https://devfeed.tech/tags/microsoft-teams.md>), [porting](<https://devfeed.tech/tags/porting.md>), [ruby](<https://devfeed.tech/tags/ruby.md>), [strava](<https://devfeed.tech/tags/strava.md>)

### AI overview

The article compares a manual 2023 port of a Strava chat bot from Slack to Discord with a 2026 port to Microsoft Teams completed with GitHub Copilot CLI. The Teams port took three days and involved authentication, app-manifest, tenant distribution, and Azure setup work.

### Source excerpt

Slava (slack-strava) connects a Strava account to a chat and posts a card for every activity, complete with map, pace, and elevation, plus commands like connect, disconnect, stats, and leaderboard. In 2023 I ported Slava to Discord by hand, evening by evening over about three weeks, and it took most of that stretch to get a working bot called Strada (discord-strava). This week I ported the same app to Microsoft Teams as Strata (teams-strava) in 3 days instead of the 3 weeks the Discord port took, using time carved out during the Microsoft Global Hackathon. By "I", I mean GitHub Copilot CLI and I. 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: 2023-07-30 23:56 Initial fork from slack-strava. 2023-08-02 23:46 Most interactions working. 2023-08-03 06:18 Remove PNG retrieval, Discord displays redirects correctly. 2023-08-03 06:26 Removed clubs. 2023-08-03 08:28 Fixed model specs. 2023-08-03 18:20 Fix API specs. 2023-08-04 19:44 Fix command specs. ... 2023-08-08 10:34 Countinously poll with 1 minute intervals. 2023-08-20 22:59 Improved install button and text. 20 commits spread across 9 evenings over roughly three weeks (there's a real job and a life in between those timestamps). All hand-typed, with GitHub Copilot's inline autocomplete helping the way it helped everyone in 2023, but no agent driving the actual porting work. The 2026 Port teams-strava's git history is a single continuous Copilot CLI session, 107 conversational turns over three calendar days, and the timestamps compress dramatically: 2026-09-14 11:05 Initial port of discord-strava to Microsoft Teams 2026-09-14 16:39 Fix local dev setup and personal-scope install handling 2026-09-14 16:43 Fix 400 error replying to personal-scope install notice 2026-09-15 08:29 Document

## Standardizing Exception Message Style in Ruby

DevFeed: [Standardizing Exception Message Style in Ruby](<https://devfeed.tech/articles/standardizing-exception-message-style-in-ruby-20535.md>)

Original publisher: [Read original article](<https://code.dblock.org/2026/09/05/standardizing-exception-message-style-in-ruby.html>)

Author: Daniel Doubrovkine (dblock@dblock.org)

Published: 2026-09-05T00:00:00Z

Content type: article

Language: en

Sources: [Daniel Doubrovkine](<https://devfeed.tech/sources/daniel-doubrovkine.md>)

Topics: [Ruby](<https://devfeed.tech/topics/ruby.md>), [Exception](<https://devfeed.tech/topics/exception.md>), [ci](<https://devfeed.tech/topics/ci.md>)

Tags: [ci](<https://devfeed.tech/tags/ci.md>), [exception](<https://devfeed.tech/tags/exception.md>), [exceptions](<https://devfeed.tech/tags/exceptions.md>), [grape](<https://devfeed.tech/tags/grape.md>), [lint](<https://devfeed.tech/tags/lint.md>), [open-source](<https://devfeed.tech/tags/open-source.md>), [plugin](<https://devfeed.tech/tags/plugin.md>), [ruby](<https://devfeed.tech/tags/ruby.md>), [standard](<https://devfeed.tech/tags/standard.md>)

### AI overview

The article explains a convention for Ruby exception messages: they should generally begin with lowercase and omit trailing punctuation because they follow the exception class name and a colon. It describes fixing seven inconsistent sites in Grape, documenting the convention, and releasing the rubocop-exception_messages RuboCop plugin to enforce casing and punctuation, with additional checks for redundant class names and interpolation markers.

### Source excerpt

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, not as a standalone sentence. Grape's own Grape::Exceptions::* classes already follow this convention. The bare raise ArgumentError, "..." calls in dsl/entity.rb, dsl/inside_route.rb, dsl/validations.rb, and validations/types/dry_type_coercer.rb didn't, so I fixed those seven sites and documented the convention in CONTRIBUTING.md, in #2909. To avoid regressions, I wrote rubocop-exception_messages, a RuboCop plugin gem with two cops: ExceptionMessages/Casing flags (and autocorrects) messages that don't start with a lowercase letter. ExceptionMessages/Punctuation flags (and autocorrects) messages with a trailing period, with an exception for a literal ellipsis ("still processing.."), which is stylistic rather than a sentence ending. Both cops recognize raise Class, "message" and raise Class.new("message") forms, and handle interpolated (dstr) messages by only checking the literal string segments (the first segment for casing, the last for punctuation), since interpolated values in the middle are out of the cop's control. # bad raise ArgumentError, 'Missing required option.' # good raise ArgumentError, 'missing required option' The gem ships as a modern RuboCop plugin (via lint_roller), so it's a one-line addition to a consuming project's .rubocop.yml: plugins: - rubocop-exception_messages rubocop-exception_messages 0.2.0 is out now, and I've added it back to Grape's own Gemfile and

## Adventures in Daylight Saving, Norfolk Island, and Time Zone Math (in Ruby)

DevFeed: [Adventures in Daylight Saving, Norfolk Island, and Time Zone Math (in Ruby)](<https://devfeed.tech/articles/adventures-in-daylight-saving-norfolk-island-and-time-zone-math-in-ruby-20534.md>)

Original publisher: [Read original article](<https://code.dblock.org/2026/08/28/adventures-in-daylight-saving-norfolk-island-and-time-zone-math-in-ruby.html>)

Author: Daniel Doubrovkine (dblock@dblock.org)

Published: 2026-08-28T00:00:00Z

Content type: article

Language: en

Sources: [Daniel Doubrovkine](<https://devfeed.tech/sources/daniel-doubrovkine.md>)

Topics: [Ruby](<https://devfeed.tech/topics/ruby.md>), [DateTime](<https://devfeed.tech/topics/datetime.md>), [bug](<https://devfeed.tech/topics/bug.md>)

Tags: [bug](<https://devfeed.tech/tags/bug.md>), [code](<https://devfeed.tech/tags/code.md>), [open-source](<https://devfeed.tech/tags/open-source.md>), [ruby](<https://devfeed.tech/tags/ruby.md>), [time](<https://devfeed.tech/tags/time.md>)

### AI overview

This article explains how several bugs in the Ruby gem distance_of_time_in_words were caused by incorrect assumptions about daylight-saving transitions and time-zone offsets. Fixes replaced DST checks and one-hour special cases with comparisons of actual UTC offsets, handling Europe/Dublin and Norfolk Island correctly.

### Source excerpt

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". Several separate bug reports against it 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. The first two fixes shipped in dotiw 5.6.0; four more followed shortly after in dotiw 5.6.1. 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 = largest - smallest d -= 1.hour if smallest.dst? && !largest.dst? d += 1.hour if !smallest.dst? && largest.dst? The idea: if a DST transition happened between the two times, Time subtraction already accounts for the wall-clock jump, so cancel it back out before splitting the duration into calendar units. That works everywhere except Ireland. Europe/Dublin uses an inverted DST scheme: its winter time is legally defined as "standard time minus one hour" rather than the more common "standard time is winter, summer is +1". Depending on whether a Time was constructed via Time.at(seconds) or datetime.to_time, dst? could report different values for the exact same instant, even though utc_offset agreed. The correction fired when it shouldn't have, and a real one-minute gap got silently zeroed out. Reproducing it doesn't even require mocking dst? -- just running the example with the right TZ set is enough: ENV['TZ'] = 'Europe/Dublin' start = Time.at(DateTime.now) finish = DateTime.now + 1.minute # => "less than 1 second" # expected: "1 minute" distance_of_time_in_words(start, finish) The fix (PR #152) was to stop asking "is this DST?" and just compare the actual offsets: def offset_decreased?(smallest, largest) smallest.utc_offset > largest.utc_offset end def offset_increased?(smallest, largest) smallest.utc_offset < largest.utc_offset

## Benchmarks Are Free Now

DevFeed: [Benchmarks Are Free Now](<https://devfeed.tech/articles/benchmarks-are-free-now-20533.md>)

Original publisher: [Read original article](<https://code.dblock.org/2026/08/15/benchmarks-are-free-now.html>)

Author: Daniel Doubrovkine (dblock@dblock.org)

Published: 2026-08-15T00:00:00Z

Content type: article

Language: en

Sources: [Daniel Doubrovkine](<https://devfeed.tech/sources/daniel-doubrovkine.md>)

Topics: [Ruby](<https://devfeed.tech/topics/ruby.md>), [Benchmark](<https://devfeed.tech/topics/benchmark.md>), [Polymorphism](<https://devfeed.tech/topics/polymorphism.md>), [GitHub Copilot CLI](<https://devfeed.tech/topics/github-copilot-cli.md>)

Tags: [ai](<https://devfeed.tech/tags/ai.md>), [benchmarks](<https://devfeed.tech/tags/benchmarks.md>), [caching](<https://devfeed.tech/tags/caching.md>), [cli](<https://devfeed.tech/tags/cli.md>), [copilot](<https://devfeed.tech/tags/copilot.md>), [open-source](<https://devfeed.tech/tags/open-source.md>), [performance](<https://devfeed.tech/tags/performance.md>), [ruby](<https://devfeed.tech/tags/ruby.md>)

### AI overview

A Ruby enum inheritance fix was correct but made subclass lookups about five times slower because merged enum hashes were rebuilt on every call. Benchmarks generated with Copilot CLI exposed the regression, and memoization restored subclass performance to the base-class level. Additional benchmarks quantified the gem's small overhead for hash-backed lookups and much larger overhead for exhaustive matching.

### Source excerpt

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 recomputes the merged hash, walking the entire ancestor chain, on every single call. There's no caching. A one-level subclass calling .value pays for building a brand new hash, on top of the superclass doing the same, every time. The test suite didn't notice because tests check correctness, not speed, and correctness was fine. I only found this because I asked Copilot CLI to add a benchmark script comparing lookups at different inheritance depths, mostly out of curiosity about how the "Benchmarks" section of the README would read next to the new feature. It took one prompt and about a minute to get a working script: class Colors include Ruby::Enum define :RED, 'red' define :GREEN, 'green' end class SubColors < Colors define :BLUE, 'blue' end class SubSubColors < SubColors define :YELLOW, 'yellow' end benchmark('base class (no inheritance)', n) { Colors.value(:RED) } benchmark('subclass (1 level)', n) { SubColors.value(:RED) } benchmark('sub-subclass (2 levels)', n) { SubSubColors.value(:RED) } The first run told the story immediately: a 1-level subclass's .value calls took roughly 5x as long as the base class, and a 2-level subclass was worse still. Nobody had written this benchmark before merging #59 because writing a throwaway benchmark script used to feel like more ceremony than it was worth for a one-off performance check. Now it's a single sentence to an agent, and the scri

## Ruby Instance Variables Are Not Inherited (and Why That Breaks Your DSL)

DevFeed: [Ruby Instance Variables Are Not Inherited (and Why That Breaks Your DSL)](<https://devfeed.tech/articles/ruby-instance-variables-are-not-inherited-and-why-that-breaks-your-dsl-20532.md>)

Original publisher: [Read original article](<https://code.dblock.org/2026/08/14/instance-variables-are-not-inherited-and-why-that-breaks-your-dsl.html>)

Author: Daniel Doubrovkine (dblock@dblock.org)

Published: 2026-08-14T00:00:00Z

Content type: article

Language: en

Sources: [Daniel Doubrovkine](<https://devfeed.tech/sources/daniel-doubrovkine.md>)

Topics: [Ruby](<https://devfeed.tech/topics/ruby.md>), [bug](<https://devfeed.tech/topics/bug.md>), [Library](<https://devfeed.tech/topics/library.md>), [Development](<https://devfeed.tech/topics/development.md>), [Rails](<https://devfeed.tech/topics/rails.md>)

Tags: [bug](<https://devfeed.tech/tags/bug.md>), [development](<https://devfeed.tech/tags/development.md>), [library](<https://devfeed.tech/tags/library.md>), [open-source](<https://devfeed.tech/tags/open-source.md>), [pull-requests](<https://devfeed.tech/tags/pull-requests.md>), [rails](<https://devfeed.tech/tags/rails.md>), [ruby](<https://devfeed.tech/tags/ruby.md>)

### AI overview

This article explains how Ruby instance variables used by the ruby-enum gem are not inherited by subclasses, causing failures involving subclassing, class reloading, and redundant module inclusion. It describes fixes shipped in ruby-enum 1.2.0, including lazy defaults and explicit merging of inherited enums.

### Source excerpt

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 ClassMethods base.instance_variable_set(:@_enum_hash, {}) base.instance_variable_set(:@_enums_by_value, {}) end This works fine for a single class. It gets interesting the moment subclasses or class reloading show up. A user opened #56, reporting DuplicateKeyError from a Rails console after a class that had already defined its enums got reloaded. It reproduces without Rails. class_body = proc do include Ruby::Enum define :RED, 'red' end Colors = Class.new(&class_body) Colors.class_eval(&class_body) # raises DuplicateKeyError Rails' development-mode autoloader re-evaluates a class body, and the second define :RED sees @_enum_hash still holding RED from the first load, so the duplicate-key check fires. The fix clears the hash whenever Ruby::Enum is included, rather than assuming it's always a fresh class. The contributor that reported the problem and submitted the fix also caught a sneakier variant: a subclass that redundantly re-includes Ruby::Enum (include is supposed to be idempotent, right?) was wiping out enums inherited from its parent, because included unconditionally reset the instance variables: class OtherSecondSubclass < FirstSubclass include Ruby::Enum # redundant, but shouldn't lose FirstSubclass's enums define :MAGENTA, 'magenta' end Thanks, Nazar, for tracking both of these down with tests. Months later, #49 surfaced a related crash: a subclass that never calls define itsel

## Generating AI Descriptions of Automated Pull Requests

DevFeed: [Generating AI Descriptions of Automated Pull Requests](<https://devfeed.tech/articles/generating-ai-descriptions-of-automated-pull-requests-20531.md>)

Original publisher: [Read original article](<https://code.dblock.org/2026/08/11/generating-ai-descriptions-of-automated-pull-requests.html>)

Author: Daniel Doubrovkine (dblock@dblock.org)

Published: 2026-08-11T00:00:00Z

Content type: tutorial

Language: en

Sources: [Daniel Doubrovkine](<https://devfeed.tech/sources/daniel-doubrovkine.md>)

Topics: [GitHub Copilot CLI](<https://devfeed.tech/topics/github-copilot-cli.md>), [GitHub Actions](<https://devfeed.tech/topics/github-actions.md>), [GitHub Copilot](<https://devfeed.tech/topics/github-copilot.md>), [Large Language Model](<https://devfeed.tech/topics/llm.md>), [Ruby](<https://devfeed.tech/topics/ruby.md>), [Slack](<https://devfeed.tech/topics/slack.md>), [changelog](<https://devfeed.tech/topics/changelog.md>), [Pull Request](<https://devfeed.tech/topics/pull-request.md>), [Open Source](<https://devfeed.tech/topics/open-source.md>), [JSON](<https://devfeed.tech/topics/json.md>), [YAML](<https://devfeed.tech/topics/yaml.md>)

Tags: [ai](<https://devfeed.tech/tags/ai.md>), [automated](<https://devfeed.tech/tags/automated.md>), [changelog](<https://devfeed.tech/tags/changelog.md>), [ci](<https://devfeed.tech/tags/ci.md>), [cli](<https://devfeed.tech/tags/cli.md>), [copilot](<https://devfeed.tech/tags/copilot.md>), [github](<https://devfeed.tech/tags/github.md>), [github-actions](<https://devfeed.tech/tags/github-actions.md>), [github-copilot](<https://devfeed.tech/tags/github-copilot.md>), [github-copilot-cli](<https://devfeed.tech/tags/github-copilot-cli.md>), [json](<https://devfeed.tech/tags/json.md>), [llm](<https://devfeed.tech/tags/llm.md>), [open-source](<https://devfeed.tech/tags/open-source.md>), [pull-request](<https://devfeed.tech/tags/pull-request.md>), [pull-requests](<https://devfeed.tech/tags/pull-requests.md>), [ruby](<https://devfeed.tech/tags/ruby.md>), [yaml](<https://devfeed.tech/tags/yaml.md>)

### AI overview

A tutorial explains how the maintainer of the slack-ruby-client library uses a scheduled GitHub Actions workflow and GitHub Copilot CLI to generate meaningful commit messages, pull request bodies, and CHANGELOG entries from diffs of regenerated Slack API code. It also covers YAML indentation, prompt files, response logging, and extracting a JSON result from Copilot CLI output.

### Source excerpt

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, we pipe that diff through an LLM and ask it to summarize what changed, then use the response as the commit message and PR body. - name: Check for changes id: changes run: | if git diff --quiet; then echo "changed=false" >> "$GITHUB_OUTPUT" else echo "changed=true" >> "$GITHUB_OUTPUT" fi - name: Prepare diff for AI summary if: steps.changes.outputs.changed == 'true' run: | git diff --stat | sed 's/^/ /' > /tmp/diff_stat.txt git diff | head -c 20000 | sed 's/^/ /' > /tmp/diff.txt The sed 's/^/ /' indent isn't decorative. actions/ai-inference substitutes template variables as raw text into a prompt YAML file before parsing it, so a multi-line diff starting at column 0 breaks the indentation of the enclosing content: |- block scalar. Pre-indenting the file to match keeps the YAML valid no matter what the diff looks like. The Prompt File actions/ai-inference supports .prompt.yml files, a small convention for keeping the system/user prompt out of the workflow YAML. messages: - role: system content: |- You write CHANGELOG entries describing an automated API update to slack-ruby-client, a Ruby gem whose Web API endpoint methods, argument validations, specs, and bin commands are code-generated from vendored Slack API method definitions (via a git submodule and rake task). Given a diffstat and a diff of the regenerated files, respond with ONLY a single JSON object (no markdown code fence

## Coding with AI Agents is Now a Baseline Expectation for Managers

DevFeed: [Coding with AI Agents is Now a Baseline Expectation for Managers](<https://devfeed.tech/articles/coding-with-ai-agents-is-now-a-baseline-expectation-for-managers-20530.md>)

Original publisher: [Read original article](<https://code.dblock.org/2026/05/05/coding-with-ai-agents-is-now-a-baseline-expectation-for-managers.html>)

Author: Daniel Doubrovkine (dblock@dblock.org)

Published: 2026-05-05T00:00:00Z

Content type: opinion

Language: en

Sources: [Daniel Doubrovkine](<https://devfeed.tech/sources/daniel-doubrovkine.md>)

Topics: [Artificial Intelligence](<https://devfeed.tech/topics/ai.md>), [AI Agent](<https://devfeed.tech/topics/ai-agent.md>), [Developer experience](<https://devfeed.tech/topics/developer-experience.md>), [Code](<https://devfeed.tech/topics/code.md>), [Vibe coding](<https://devfeed.tech/topics/vibe-coding.md>)

Tags: [ai](<https://devfeed.tech/tags/ai.md>), [ai-agents](<https://devfeed.tech/tags/ai-agents.md>), [copilot](<https://devfeed.tech/tags/copilot.md>), [developer-experience](<https://devfeed.tech/tags/developer-experience.md>), [management](<https://devfeed.tech/tags/management.md>), [people](<https://devfeed.tech/tags/people.md>), [pull-requests](<https://devfeed.tech/tags/pull-requests.md>), [vibe-coding](<https://devfeed.tech/tags/vibe-coding.md>)

### AI overview

An engineering manager argues that managers must become active AI practitioners to lead AI transformation. The author describes joining a small engineering team, using Copilot to contribute to the project, and producing 89 pull requests while also breaking production features and identifying gaps in integration testing.

### Source excerpt

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 up with the pace of evolution of this technology. Therefore, one cannot drive AI transformation without being an active AI practitioner. But what does being an active AI practitioner look like for a manager? To find out, I landed myself into a small Engineering team (7 people) within my 100+ people organization five weeks ago. This small group develops an essential internal Microsoft system used by every single Engineer at the company. I wanted to learn firsthand about their challenges by onboarding into the project, and maybe contribute 2-3 PRs myself. I decided to dedicate my Copious Free Time™ to this work, to only use Copilot (you can use Claude, or another CLI, too), and to attend the team's daily standup. Before I talk about what I did or learned, I must apologize to the team for giving them a heart attack upon my arrival. It's rare that a VP of Engineering, many levels "above", inserts themselves into any team unless there's some huge, urgent, problem. This wasn't an issue with the specific team, but transforming the way we work remains absolutely urgent, and it's my responsibility to do everything I can to make it happen. Initially, I expected to make 2-3 pull requests. Instead, I was able to make 89 (!), broke at least two production features, but also shipped some meaningful improvements. These fell into several categories. Improved developer experience. I fixed sever

## How I Made $360 by Serving Markdown to A.I. agents from My Jekyll Blog

DevFeed: [How I Made $360 by Serving Markdown to A.I. agents from My Jekyll Blog](<https://devfeed.tech/articles/how-i-made-360-by-serving-markdown-to-a-i-agents-from-my-jekyll-blog-20529.md>)

Original publisher: [Read original article](<https://code.dblock.org/2026/04/11/how-i-made-360-dollars-by-serving-markdown-from-my-jekyll-blog.html>)

Author: Daniel Doubrovkine (dblock@dblock.org)

Published: 2026-04-11T00:00:00Z

Content type: opinion

Language: en

Sources: [Daniel Doubrovkine](<https://devfeed.tech/sources/daniel-doubrovkine.md>)

Topics: [Jekyll](<https://devfeed.tech/topics/jekyll.md>), [Markdown](<https://devfeed.tech/topics/markdown.md>)

Tags: [ai](<https://devfeed.tech/tags/ai.md>), [blogging](<https://devfeed.tech/tags/blogging.md>), [jekyll](<https://devfeed.tech/tags/jekyll.md>), [markdown](<https://devfeed.tech/tags/markdown.md>), [tech](<https://devfeed.tech/tags/tech.md>)

### AI overview

The author describes adding clean Markdown versions of Jekyll blog posts so AI agents can fetch them instead of parsing HTML. Thirty days later, a linked Monarch referral produced 18 signups, 12 paid conversions, and $360 in referral earnings, with no change in SEO traffic.

### Source excerpt

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 think much of it. Exactly thirty days later - the length of Monarch's free trial -- I started getting a stream of referral conversion notifications without any change in SEO traffic. Eighteen signups. Twelve converted to paid. At $30/referral, that's $360. We live in the future. How I Made $360 by Serving Markdown to A.I. agents from My Jekyll Blog was originally published by Daniel Doubrovkine at code.dblock.org | tech blog on April 11, 2026.

## Opening Proprietary Code to Contributors Checklist

DevFeed: [Opening Proprietary Code to Contributors Checklist](<https://devfeed.tech/articles/opening-proprietary-code-to-contributors-checklist-20528.md>)

Original publisher: [Read original article](<https://code.dblock.org/2026/04/08/opening-proprietary-code-to-contributors-checklist.html>)

Author: Daniel Doubrovkine (dblock@dblock.org)

Published: 2026-04-08T00:00:00Z

Content type: tutorial

Language: en

Sources: [Daniel Doubrovkine](<https://devfeed.tech/sources/daniel-doubrovkine.md>)

Topics: [ai-coding](<https://devfeed.tech/topics/ai-coding.md>), [pull-requests](<https://devfeed.tech/topics/pull-requests.md>), [Open Source](<https://devfeed.tech/topics/open-source.md>), [ci](<https://devfeed.tech/topics/ci.md>), [GitHub](<https://devfeed.tech/topics/github.md>)

Tags: [ai](<https://devfeed.tech/tags/ai.md>), [ai-coding](<https://devfeed.tech/tags/ai-coding.md>), [ci](<https://devfeed.tech/tags/ci.md>), [contributions](<https://devfeed.tech/tags/contributions.md>), [engineering](<https://devfeed.tech/tags/engineering.md>), [github](<https://devfeed.tech/tags/github.md>), [how-to](<https://devfeed.tech/tags/how-to.md>), [open-source](<https://devfeed.tech/tags/open-source.md>), [pull-request](<https://devfeed.tech/tags/pull-request.md>), [tests](<https://devfeed.tech/tags/tests.md>)

### AI overview

The article presents a checklist for making proprietary software repositories easier and safer for newcomers and customer contributors to use. It recommends a discoverable CONTRIBUTING.md, a simple getting-started path, automated tests in CI, locally runnable minimum versions, documented deployment processes, and a GitHub fork-and-pull-request workflow. It frames AI coding tools as lowering barriers to meaningful contributions.

### Source excerpt

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 without domain or codebase expertise with good ideas to see them to light. At work, I asked my team to actively seek contributions from our customers with an active engagement for our proprietary code. Inspired by the open-source model, we are reviewing our systems from the angle of a newcomer easily contributing to them. Here's a checklist to apply to any repo. Create a single landing CONTRIBUTING.md that welcomes contributors and encourages them to contribute while insisting on higher standards, such as needing to write automated tests with every contribution. Make your source code repo discoverable. Your source code README, and your product or tool should have a visible link to your single CONTRIBUTING.md. Include a simple to follow "Getting Started" checklist in CONTRIBUTING.md that enables any developer at the company to make a trivial change in the application and submit a pull request. Ensure that your project has an automated test suite that runs in CI, is trivial to run locally, has few to no dependencies, and has enough coverage to give confidence to any new contributor that they did not break key functionality. Evolve every project to a state where a minimum viable version of it can run locally without the need to ask for permissions, certificates, or keys, install or get access to unnecessary dependencies, removing those barriers from the new contributor path. Describe h

## AI Slop: A Slack API Rate Limiting Disaster

DevFeed: [AI Slop: A Slack API Rate Limiting Disaster](<https://devfeed.tech/articles/ai-slop-a-slack-api-rate-limiting-disaster-20527.md>)

Original publisher: [Read original article](<https://code.dblock.org/2026/03/12/ai-slop-a-slack-api-rate-limiting-disaster.html>)

Author: Daniel Doubrovkine (dblock@dblock.org)

Published: 2026-03-12T00:00:00Z

Content type: article

Language: en

Sources: [Daniel Doubrovkine](<https://devfeed.tech/sources/daniel-doubrovkine.md>)

Topics: [Slack](<https://devfeed.tech/topics/slack.md>), [API](<https://devfeed.tech/topics/api.md>), [Code](<https://devfeed.tech/topics/code.md>)

Tags: [ai](<https://devfeed.tech/tags/ai.md>), [api](<https://devfeed.tech/tags/api.md>), [async](<https://devfeed.tech/tags/async.md>), [code](<https://devfeed.tech/tags/code.md>), [concurrent](<https://devfeed.tech/tags/concurrent.md>), [distributed-system](<https://devfeed.tech/tags/distributed-system.md>), [rate-limiting](<https://devfeed.tech/tags/rate-limiting.md>), [retry](<https://devfeed.tech/tags/retry.md>), [script](<https://devfeed.tech/tags/script.md>), [slack](<https://devfeed.tech/tags/slack.md>)

### AI overview

The article examines an AI-generated cleanup job for closing old Slack group DM conversations. Because Slack's conversations.close endpoint has a global rate limit of one request per second, the implementation could exhaust the limit and disrupt other API calls. The author describes mitigating the issue with scheduled execution, an opt-in setting, a slow-drain script, and limiting the number of DMs closed concurrently.

### Source excerpt

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) update_attributes!(closed_at: Time.now.utc) end This code looks pretty great and completely breaks the app. Slack's API has a global rate limit of 1 request per second for the conversations.close endpoint. So, when this job runs against a workspace with hundreds of old conversations, it immediately hits the rate limit. Worse, because rate limits are global across all endpoints, it takes down the entire application. Every other API call -- posting messages, fetching user info, everything -- starts failing. I asked AI to address this. It made the fix catastrophically worse. def close! # ... existing code ... begin slack_client.conversations_close(channel: conversation_id) rescue Slack::Web::Api::Errors::TooManyRequests => e sleep_time = e.retry_after || 60 sleep(sleep_time) retry end # ... rest of method ... end In socketry/async, sleep() blocks the entire fiber and prevents other concurrent operations from executing. You should use Async::Task.sleep() instead, but that still doesn't solve the fundamental architectural problem of making hundreds of sequential API calls. What made this particularly insidious is that the assistant wrote some pretty professional-looking code and seemingly handled obvious edge cases. Yet, it failed to consider the distributed system constraints or global invariants. The rate limiting problem wasn't visible in the local scope of the method - it was a system-wide conc

## Right-Sizing Engineering Teams for AI

DevFeed: [Right-Sizing Engineering Teams for AI](<https://devfeed.tech/articles/right-sizing-engineering-teams-for-ai-20526.md>)

Original publisher: [Read original article](<https://code.dblock.org/2026/03/11/right-sizing-engineering-teams-for-ai.html>)

Author: Daniel Doubrovkine (dblock@dblock.org)

Published: 2026-03-11T00:00:00Z

Content type: opinion

Language: en

Sources: [Daniel Doubrovkine](<https://devfeed.tech/sources/daniel-doubrovkine.md>)

Topics: [Artificial Intelligence](<https://devfeed.tech/topics/ai.md>), [engineering-culture](<https://devfeed.tech/topics/engineering-culture.md>), [Code review](<https://devfeed.tech/topics/code-review.md>), [ai-coding](<https://devfeed.tech/topics/ai-coding.md>), [GitHub Copilot](<https://devfeed.tech/topics/github-copilot.md>), [pull-requests](<https://devfeed.tech/topics/pull-requests.md>), [Claude](<https://devfeed.tech/topics/claude.md>), [cursor](<https://devfeed.tech/topics/cursor.md>)

Tags: [ai](<https://devfeed.tech/tags/ai.md>), [ai-assistants](<https://devfeed.tech/tags/ai-assistants.md>), [ai-coding](<https://devfeed.tech/tags/ai-coding.md>), [ai-tools](<https://devfeed.tech/tags/ai-tools.md>), [claude](<https://devfeed.tech/tags/claude.md>), [code-review](<https://devfeed.tech/tags/code-review.md>), [cursor](<https://devfeed.tech/tags/cursor.md>), [engineering](<https://devfeed.tech/tags/engineering.md>), [github](<https://devfeed.tech/tags/github.md>), [github-copilot](<https://devfeed.tech/tags/github-copilot.md>), [people](<https://devfeed.tech/tags/people.md>), [pull-requests](<https://devfeed.tech/tags/pull-requests.md>), [quality](<https://devfeed.tech/tags/quality.md>), [teams](<https://devfeed.tech/tags/teams.md>)

### AI overview

An opinion article argues that AI coding assistants increase code output without replacing the experienced judgment needed for quality and review. It recommends prioritizing senior expertise over larger mixed-experience teams when planning engineering headcount.

### Source excerpt

Before AI coding assistants, a typical engineering team of 8-10 people might have been lucky to have one or two "10x engineers", or "workhorses", the kind of engineer that both keeps project quality and feature velocity high. AI tools have solved the workhorse half of this equation, enabling massive raw output. Today, almost every engineer can produce a high volume of code with GitHub Copilot, Claude, or Cursor. But the quality half of the equation has not kept up. Teams are shipping more code, but a greater fraction of it is AI slop: plausible-looking, locally coherent, globally wrong. Code review remains a human activity. Until that changes, AI assistants cannot substitute for the senior engineer. If AI triples output but the number of senior reviewers stays the same, the ratio of experienced judgment to code produced has gotten roughly 3x worse. The instinctive response to higher individual productivity is to hire fewer people, which is correct directionally but wrong in practice if you cut experience rather than volume. The difference is not in lines of code produced; it is in the accumulated judgment applied at every decision point. A team of four or five senior engineers with AI assistants will, in my experience, outperform a team of ten mixed-experience engineers with the same tools because the ratio of judgment to output stays healthy. This has implications for how engineering leaders should think about headcount planning. A well-functioning engineering team today should be five to seven people, with at most one junior. A reliable signal that you've gotten this wrong: pull requests that sit unreviewed for days, not because people are busy, but because no one feels confident enough to approve them. Smaller, more experienced engineering teams are not a new idea - Fred Brooks noted that you cannot make a late project earlier by adding people. What AI has done is make the argument sharper and more urgent. When every engineer can produce the volume that once requ

## The Golden Ratio of Manager to IC

DevFeed: [The Golden Ratio of Manager to IC](<https://devfeed.tech/articles/the-golden-ratio-of-manager-to-ic-20525.md>)

Original publisher: [Read original article](<https://code.dblock.org/2026/02/04/the-golden-ratio-of-manager-to-ic.html>)

Author: Daniel Doubrovkine (dblock@dblock.org)

Published: 2026-02-04T00:00:00Z

Content type: opinion

Language: en

Sources: [Daniel Doubrovkine](<https://devfeed.tech/sources/daniel-doubrovkine.md>)

Topics: [Meta](<https://devfeed.tech/topics/meta.md>), [AI Engineering](<https://devfeed.tech/topics/ai-engineering.md>), [Artificial Intelligence](<https://devfeed.tech/topics/ai.md>), [Software](<https://devfeed.tech/topics/software.md>)

Tags: [ai-assistants](<https://devfeed.tech/tags/ai-assistants.md>), [ai-engineering](<https://devfeed.tech/tags/ai-engineering.md>), [meta](<https://devfeed.tech/tags/meta.md>), [opinion](<https://devfeed.tech/tags/opinion.md>), [organization](<https://devfeed.tech/tags/organization.md>), [people](<https://devfeed.tech/tags/people.md>), [teams](<https://devfeed.tech/tags/teams.md>)

### AI overview

This opinion examines Meta's reported plan for an applied AI engineering organization with up to 50 employees per manager. It argues that flatter structures may reduce unnecessary management layers and that AI assistants are increasing individual contributors' capabilities, while suggesting that managerial roles must continue to evolve.

### Source excerpt

In today's shocker, Meta is to "create a new applied AI engineering organization aiming for an ultra-flat structure of up to 50 employees to one manager". Like all software engineers I, too, tend to apply a data-driven, mathematical approach to every problem in the world. Yet I would have chosen a more romantic number and applied the golden ratio: roughly 1.6:1, the proportion that shows up in seashells, galaxies, and every second slide about "natural elegance", rather than 50:1, a measure that feels less like harmony and more like a spreadsheet's idea of efficiency. The idea of flattening an organization is not new and can be a good one. I know plenty of managers who have not done any individual contributor work, code or otherwise, in years. This is particularly striking with former strong coders who are promoted to managerial roles. After 2-3 cycles of promotions they are so far detached from what's happening at the individual-contributor level that they become 100% overhead, spending their entire life in meetings and actively preventing real work from being done. It's natural to want to eliminate layers of such people as they simply don't have any impact. And so, the real news at Meta is that it's fighting its own organization design in which, at least in some teams according to my friends who work or have worked there, people managers are discouraged from doing deep technical work, don't own much beyond process, and mostly serve as reporting-structure placeholders. Another reason to flatten an organization is the introduction of AI assistants that have created a major shift in the capabilities of individual contributors. Two years ago you could maybe find one single "10x engineer" in every team--someone who has dramatically higher velocity than their peers. A good manager would recognize these extraordinary abilities, make such an individual their right hand and technical partner, share the responsibility of advancing a project, create effective mentorship, and h

## Serving Markdown for AI Agents in Jekyll

DevFeed: [Serving Markdown for AI Agents in Jekyll](<https://devfeed.tech/articles/serving-markdown-for-ai-agents-in-jekyll-20524.md>)

Original publisher: [Read original article](<https://code.dblock.org/2026/01/15/serving-markdown-for-ai-agents.html>)

Author: Daniel Doubrovkine (dblock@dblock.org)

Published: 2026-01-15T00:00:00Z

Content type: tutorial

Language: en

Sources: [Daniel Doubrovkine](<https://devfeed.tech/sources/daniel-doubrovkine.md>)

Topics: [Jekyll](<https://devfeed.tech/topics/jekyll.md>), [Markdown](<https://devfeed.tech/topics/markdown.md>), [Artificial Intelligence](<https://devfeed.tech/topics/ai.md>), [GitHub Actions](<https://devfeed.tech/topics/github-actions.md>), [GitHub](<https://devfeed.tech/topics/github.md>)

Tags: [agents](<https://devfeed.tech/tags/agents.md>), [ai](<https://devfeed.tech/tags/ai.md>), [ai-agents](<https://devfeed.tech/tags/ai-agents.md>), [deployment](<https://devfeed.tech/tags/deployment.md>), [github-actions](<https://devfeed.tech/tags/github-actions.md>), [jekyll](<https://devfeed.tech/tags/jekyll.md>), [markdown](<https://devfeed.tech/tags/markdown.md>)

### AI overview

The article explains how a Jekyll blog on GitHub Pages serves each post's source Markdown alongside its HTML so AI agents can access cleaner, structured content. A GitHub Actions workflow copies the Markdown files to matching .md URLs and adds an alternate link in the HTML head for discovery.

### Source excerpt

Dries Buytaert recently wrote about The Third Audience. For decades, websites have targeted two audiences: humans and search engines. AI agents are now the third audience, and most websites aren't optimized for them yet. AI agents prefer clean, structured content over HTML. Markdown is ideal - it's readable, semantic, and free of navigation chrome. So I made this blog serve its source markdown files alongside the HTML. How It Works For every post like /2026/01/15/serving-markdown-for-ai-agents.html, you can now fetch the source at /2026/01/15/serving-markdown-for-ai-agents.md. AI agents can discover this via a <link> tag in the HTML head: <link href="serving-markdown-for-ai-agents.md" type="text/markdown" rel="alternate" title="Markdown"> Implementation Since this is a GitHub Pages blog, custom Jekyll plugins don't work. Instead, I added a GitHub Actions workflow that builds Jekyll and copies the markdown source files to _site before deployment. The workflow extracts the date and slug from each post filename and copies it to the matching URL path with a .md extension. Should You Do This? Dries raises a valid concern: are we just making it easier for AI companies to use our content without sending traffic back? Perhaps. But AI agents are already crawling our sites. Giving them cleaner input might lead to better attribution and more accurate responses that reference our work. The web has always been about making information accessible. This is just the next evolution. Plus, we will soon all work for AI anyway, so might as well get on its good side. Meta This entire feature was implemented by Claude, and this post was written by it as well. Added GitHub Actions workflow to deploy and serve markdown. Use Ruby 3.4 in deploy workflow. Also serve page markdown files. Added link rel alternate for markdown discovery. Moved markdown link to post layout. Fixed markdown alternate URL to remove .html. Serving Markdown for AI Agents. Serving Markdown for AI Agents in Jekyll was o

## Turning Hypermedia APIs into MCPs

DevFeed: [Turning Hypermedia APIs into MCPs](<https://devfeed.tech/articles/turning-hypermedia-apis-into-mcps-20523.md>)

Original publisher: [Read original article](<https://code.dblock.org/2025/09/18/turning-hypermedia-apis-into-mcps.html>)

Author: Daniel Doubrovkine (dblock@dblock.org)

Published: 2025-09-18T09:00:00Z

Content type: tutorial

Language: en

Sources: [Daniel Doubrovkine](<https://devfeed.tech/sources/daniel-doubrovkine.md>)

Topics: [API](<https://devfeed.tech/topics/api.md>), [Model Context Protocol (MCP)](<https://devfeed.tech/topics/model-context-protocol-mcp.md>), [MCP Server](<https://devfeed.tech/topics/mcp-server.md>), [REST API](<https://devfeed.tech/topics/rest-api.md>), [Ruby](<https://devfeed.tech/topics/ruby.md>), [Open Source](<https://devfeed.tech/topics/open-source.md>), [Slack](<https://devfeed.tech/topics/slack.md>)

Tags: [ai](<https://devfeed.tech/tags/ai.md>), [api-design](<https://devfeed.tech/tags/api-design.md>), [apis](<https://devfeed.tech/tags/apis.md>), [hypermedia](<https://devfeed.tech/tags/hypermedia.md>), [mcp](<https://devfeed.tech/tags/mcp.md>), [mcp-server](<https://devfeed.tech/tags/mcp-server.md>), [mcps](<https://devfeed.tech/tags/mcps.md>), [model-context-protocol](<https://devfeed.tech/tags/model-context-protocol.md>), [open-source](<https://devfeed.tech/tags/open-source.md>), [rest](<https://devfeed.tech/tags/rest.md>), [rest-api](<https://devfeed.tech/tags/rest-api.md>), [ruby](<https://devfeed.tech/tags/ruby.md>), [slack](<https://devfeed.tech/tags/slack.md>)

### AI overview

This tutorial shows how to expose a Hypermedia API as an MCP server using the Ruby gem hyperclient-mcp. It uses the open-source S'Up Slack bot to demonstrate resource discovery, starting the server, connecting it to Claude, and querying bot and team information.

### Source excerpt

I've written and talked extensively about Hypermedia APIs for about a decade. The HATEOAS constraints in representing resources for a RESTful API has numerous advantages in API design, readability, discoverability, and performance over a hand-rolled REST API. Unfortunately, adoption of Hypermedia APIs is very low compared to, for example GraphQL, but I find the implementation a lot simpler and continue adding Hypermedia APIs to my pet projects. At its root a Hypermedia API looks like so. { "_links": { "self": { "href": "https://sup2.playplay.io/api" }, "status": { "href": "https://sup2.playplay.io/api/status" }, "team": { "href": "https://sup2.playplay.io/api/teams/{id}", "templated": true }, ... } } The above example is an extract from this API. For this post we will use one of my Slack bots called S'Up, which generates fresh triads of team members in Slack to meet for coffee every week in an informal standup. The bot is open-source and you can try it here. Because the Hypermedia API structure is fixed with only "resources" and "links" (and similar to API frameworks such as GraphQL) you don't need an application-specific client to interact with the API. In Ruby, the most popular generic Hypermedia client is Hyperclient. With the API above, a client can retrieve the bot status, and using an API token obtained from an existing installation some team information. api = Hyperclient.new('https://sup2.playplay.io/api') do |client| client.headers['X-Access-Token'] = ENV.fetch('TOKEN', nil) end status = api.status puts "Bot is #{status.ping['presence']['presence']}." team = api.team(id: '1234') puts "Team name is '#{team.name}'." This will output "Bot is online." and "Team name is 'dblock'." for my installation. In this case, the generic client knew nothing about "team" - it was discovered programmatically and was, in a way, self-documenting. Unsurprisingly, this idea of templated resources is reused almost exactly the same way in the model context protocol (MCP). Therefor

## Using AI to Reduce the Burden of Status Reporting

DevFeed: [Using AI to Reduce the Burden of Status Reporting](<https://devfeed.tech/articles/using-ai-to-reduce-the-burden-of-status-reporting-20522.md>)

Original publisher: [Read original article](<https://code.dblock.org/2025/08/29/using-ai-to-reduce-the-burden-of-status-reporting.html>)

Author: Daniel Doubrovkine (dblock@dblock.org)

Published: 2025-08-29T09:00:00Z

Content type: opinion

Language: en

Sources: [Daniel Doubrovkine](<https://devfeed.tech/sources/daniel-doubrovkine.md>)

Topics: [Artificial Intelligence](<https://devfeed.tech/topics/ai.md>), [Development](<https://devfeed.tech/topics/development.md>), [Software](<https://devfeed.tech/topics/software.md>)

Tags: [ai](<https://devfeed.tech/tags/ai.md>), [management](<https://devfeed.tech/tags/management.md>), [people](<https://devfeed.tech/tags/people.md>), [project-management](<https://devfeed.tech/tags/project-management.md>), [reporting](<https://devfeed.tech/tags/reporting.md>), [software-development](<https://devfeed.tech/tags/software-development.md>)

### AI overview

The article argues that hierarchical software organizations can create excessive status-reporting work and meetings. It proposes using AI to automate most of that reporting while preserving the coordination benefits of organizational hierarchy.

### Source excerpt

One of the negative side effects of having any top-down command structure is that every additional layer risks introducing a new level of bureaucracy, which then actively subtracts value. Consider the Politburo of the USSR with dozens of offices of skilled bureaucrats. Because most produced no actual value, the best apparatchiks spent their entire time managing up to remain in control. Individuals who tried to upend the system either became exhausted and gave up, or were deported to Siberia. The Soviet example also applies to software development. Consider a typical CEO -> CTO -> VP of Engineering -> Director -> Manager -> Technical Lead -> IC command structure. Each project team consists of a technical lead and a handful of individual contributors. The people committed to the project do not need status reports to know what's going on, they are actively working on it. The stakeholders and the hierarchy, however, naturally want progress and status reports. This gets aggregated at Director level, then at VP level, the CTO, and finally the CEO. From the point of view of every manager in this hierarchy collecting status and asking questions feels very natural. After all, it's much more efficient to get a summary of what's going on from a line manager than to read every status update or code change yourself. You have the power to call a meeting, have things explained and summarized to you. You also have the authority to make snap judgments based on this carefully crafted summary and partial information, and the responsibility to tell the team to focus on something they likely already know. This system quickly promotes those who are good at reporting the things their manager wants to hear, and encourages political behavior as people learn to navigate the system rather than focus on outcomes. It's worth acknowledging that these hierarchical structures exist for good reasons. Large organizations need coordination, resource allocation, and strategic alignment. The problem isn't hi

## Don't Tell Engineers What to Do

DevFeed: [Don't Tell Engineers What to Do](<https://devfeed.tech/articles/don-t-tell-engineers-what-to-do-20519.md>)

Original publisher: [Read original article](<https://code.dblock.org/2025/07/30/dont-tell-engineers-what-to-do.html>)

Author: Daniel Doubrovkine (dblock@dblock.org)

Published: 2025-07-30T08:00:00Z

Content type: opinion

Language: en

Sources: [Daniel Doubrovkine](<https://devfeed.tech/sources/daniel-doubrovkine.md>)

Topics: [engineering-culture](<https://devfeed.tech/topics/engineering-culture.md>), [Software Engineering](<https://devfeed.tech/topics/software-engineering.md>)

Tags: [advice](<https://devfeed.tech/tags/advice.md>), [engineering](<https://devfeed.tech/tags/engineering.md>), [management](<https://devfeed.tech/tags/management.md>), [people](<https://devfeed.tech/tags/people.md>), [software](<https://devfeed.tech/tags/software.md>), [software-engineering](<https://devfeed.tech/tags/software-engineering.md>)

### AI overview

This opinion article argues that managers should not simply dictate technical decisions to engineers. It uses the Challenger disaster and several software failures to illustrate the risks of sidelining engineering judgment, then distinguishes managerial direction from peer-level disagreement and commitment.

### Source excerpt

A famous example where telling Engineers what to do backfired was the Space Shuttle Challenger disaster in 1986. Engineers at Morton Thiokol, the contractor responsible for the shuttle's solid rocket boosters, warned NASA management that the O-rings in the boosters could fail in cold weather. The night before launch, engineers strongly recommended delaying the launch due to unusually low temperatures. Management, under pressure to proceed, overruled the engineers' concerns and told them to "make a recommendation based on data, not emotion." Eventually, management told the engineers to sign off on the launch, despite their objections. The shuttle launched in cold weather, the O-rings failed, and the Challenger exploded, killing all seven astronauts on board. This wasn't a software problem, but plenty of software engineering disasters are documented. The Knight Capital Group trading loss (2012), where rushed deployment caused a $460M loss, the Ariane 5 rocket failure (1996), where reused code not designed for the new rocket led to its destruction, the Therac-25 radiation overdoses (1985-87), where ignoring software safety warnings resulted in patient deaths, and the Healthcare.gov launch (2013), where ignoring technical advice led to a high-profile, catastrophic rollout. In each case, sidelining engineering judgment in favor of business or schedule pressures led to major failures. I bet you have your own disaster story. To quote you, "I told you so!". Yet, engineering managers continue telling engineers what to do every day. And not just engineers - all subordinates. Sometimes, it's time pressure. More often it is because managers are also engineers, and occasionally more experienced, so we think we just know better. Do we? I tell my direct reports that there's nothing I can make them do, but that there may be real consequences. I once refused to do something highly problematic my manager asked me to do, and instead said I'll think about it. It was a clever response,

## Using Claude Code with Google Sheets

DevFeed: [Using Claude Code with Google Sheets](<https://devfeed.tech/articles/using-claude-code-with-google-sheets-20520.md>)

Original publisher: [Read original article](<https://code.dblock.org/2025/07/30/using-claude-code-with-google-sheets.html>)

Author: Daniel Doubrovkine (dblock@dblock.org)

Published: 2025-07-30T01:00:00Z

Content type: tutorial

Language: en

Sources: [Daniel Doubrovkine](<https://devfeed.tech/sources/daniel-doubrovkine.md>)

Topics: [Claude Code](<https://devfeed.tech/topics/claude-code.md>), [Google](<https://devfeed.tech/topics/google.md>), [API](<https://devfeed.tech/topics/api.md>), [Command-line interface](<https://devfeed.tech/topics/cli.md>), [cURL](<https://devfeed.tech/topics/curl.md>), [Code](<https://devfeed.tech/topics/code.md>), [Cloud](<https://devfeed.tech/topics/cloud.md>)

Tags: [ai](<https://devfeed.tech/tags/ai.md>), [api](<https://devfeed.tech/tags/api.md>), [claude](<https://devfeed.tech/tags/claude.md>), [claude-code](<https://devfeed.tech/tags/claude-code.md>), [cli](<https://devfeed.tech/tags/cli.md>), [cloud](<https://devfeed.tech/tags/cloud.md>), [command-line](<https://devfeed.tech/tags/command-line.md>), [curl](<https://devfeed.tech/tags/curl.md>), [google](<https://devfeed.tech/tags/google.md>), [install](<https://devfeed.tech/tags/install.md>), [tools](<https://devfeed.tech/tags/tools.md>), [troubleshooting](<https://devfeed.tech/tags/troubleshooting.md>)

### AI overview

This tutorial shows how to connect Claude Code to Google Sheets using Google Cloud, the Google Sheets API, Google Cloud CLI authentication with Drive access, and a curl request. It also demonstrates asking Claude Code to analyze spreadsheet data and describes authentication and project-configuration troubleshooting.

### Source excerpt

Let's get Claude Code to connect to Google Sheets. We'll use this Google Sample Spreadsheet. Go to the Google Cloud Console, create a new project, and enable the Google Sheets API on it. Install the Google Cloud CLI. brew install google-cloud-sdk Choose the newly created project on the command line. ~$ gcloud config set project gsheetscli Updated property [core/project]. Login again, but this time enable Google Drive access. gcloud auth login --enable-gdrive-access You can now access the spreadsheet with curl. curl -H "Authorization: Bearer $(gcloud auth print-access-token)" \ "https://sheets.googleapis.com/v4/spreadsheets/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/values/Class%20Data" { "range": "'Class Data'!A1:V101", "majorDimension": "ROWS", "values": [ [ "Student Name", "Gender", "Class Level", "Home State", "Major", "Extracurricular Activity" ], ... ] } Let's ask Claude to do something useful. Let's ask Claude to do something useful. ~$ claude ╭───────────────────────────────────────────────────╮ │ ✻ Welcome to Claude Code! │ ╰───────────────────────────────────────────────────╯ > analyze the distribution of extracurricular activities in https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit?gid=0#gid=0 ⏺ I'll analyze the distribution of extracurricular activities from the Google Sheets document you provided. ⏺ Fetch(https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit?gid=0#gid=0) ⎿ Received 193.5KB (200 OK) ⏺ The extracurricular activities distribution shows perfect balance: Activity Distribution: - Drama Club: 5 students (16.7%) - Basketball: 5 students (16.7%) - Debate: 5 students (16.7%) - Lacrosse: 5 students (16.7%) - Baseball: 5 students (16.7%) - Track & Field: 5 students (16.7%) Total: 30 students across 6 activities Each activity has exactly 5 students participating, resulting in an even distribution with no activity being more popular than others in this dataset. Whoa. Troubles

## I Failed to Implement the Diameter of a Binary Tree in a Coding Interview

DevFeed: [I Failed to Implement the Diameter of a Binary Tree in a Coding Interview](<https://devfeed.tech/articles/i-failed-to-implement-the-diameter-of-a-binary-tree-in-a-coding-interview-20517.md>)

Original publisher: [Read original article](<https://code.dblock.org/2025/06/21/i-failed-to-implement-the-diameter-of-a-binary-tree-in-a-coding-interview.html>)

Author: Daniel Doubrovkine (dblock@dblock.org)

Published: 2025-06-21T01:00:00Z

Content type: opinion

Language: en

Sources: [Daniel Doubrovkine](<https://devfeed.tech/sources/daniel-doubrovkine.md>)

Topics: [LeetCode](<https://devfeed.tech/topics/leetcode.md>), [coding](<https://devfeed.tech/topics/coding.md>), [Code](<https://devfeed.tech/topics/code.md>)

Tags: [advice](<https://devfeed.tech/tags/advice.md>), [ai](<https://devfeed.tech/tags/ai.md>), [coding](<https://devfeed.tech/tags/coding.md>), [interview](<https://devfeed.tech/tags/interview.md>), [interviewing](<https://devfeed.tech/tags/interviewing.md>), [junior](<https://devfeed.tech/tags/junior.md>), [principal-engineer](<https://devfeed.tech/tags/principal-engineer.md>)

### AI overview

A Principal Engineer recounts freezing during a basic coding interview despite decades of programming experience. The article offers advice for recovering from interview failure, including maintaining perspective, practicing, and recognizing the role of luck in LeetCode-style interviews.

### Source excerpt

Six months ago I failed a basic coding interview at a FAANG. Yes, I was a Principal Engineer, and yes, I was paid absurd amounts of money, yet I couldn't implement a diameter of a binary tree as a "warm up" exercise, 10 lines of code. The interviewer was very nice about it, and was equally surprised. You see, I've been coding for 35 years, and I am not "rusty" at it, which is a typical excuse for senior ICs bombing LeetCode interviews. At that time I was writing code every day. I simply froze, and couldn't do it. All I could think of was "why the hell am I here doing this to myself?". I couldn't turn my brain around, apologized, cut the interview short, and, while I did ace several coding interviews at other companies for IC roles, went back to management for which I am, obviously, more qualified. Did you just bomb a coding interview? Here's some practical advice of how to deal with it. First, keep your head high and do not let this negative experience get to you. It feels awful, but unless you are blogging about your failure to implement the diameter of a binary tree, nobody other than you and the interviewer will ever know. Most likely the interviewer was also a half asleep drone deep inside the borg, administering a coding puzzle every day to the next sucker, so they will forget about you the next day. I see a lot of junior Engineers affected by failing at these. If you are recently out of college, it's helpful to think of these interviews as a school test, such as an SAT. I never had to take SATs (my children did), but most European schools have oral exams where you get a ticket from a jar with the problem statement. I had slept through too many alarms to attend an 8 AM physics class around 1996 because of a grueling late night partying schedule, and only really knew maybe half the material. Luckily 2/3 questions in my ticket overlapped with those classes I did attend, and so I did well. If you don't like school and prefer video games, think of these interviews

## Using Claude-Swarm to Upgrade Ruby Projects

DevFeed: [Using Claude-Swarm to Upgrade Ruby Projects](<https://devfeed.tech/articles/using-claude-swarm-to-upgrade-ruby-projects-20518.md>)

Original publisher: [Read original article](<https://code.dblock.org/2025/06/21/using-claude-swarm-to-upgrade-ruby-projects.html>)

Author: Daniel Doubrovkine (dblock@dblock.org)

Published: 2025-06-21T01:00:00Z

Content type: tutorial

Language: en

Sources: [Daniel Doubrovkine](<https://devfeed.tech/sources/daniel-doubrovkine.md>)

Topics: [Ruby](<https://devfeed.tech/topics/ruby.md>), [upgrade](<https://devfeed.tech/topics/upgrade.md>), [Claude Code](<https://devfeed.tech/topics/claude-code.md>), [AI Development](<https://devfeed.tech/topics/ai-development.md>), [Command-line interface](<https://devfeed.tech/topics/cli.md>), [Test-driven development](<https://devfeed.tech/topics/tdd.md>), [Code review](<https://devfeed.tech/topics/code-review.md>)

Tags: [ai](<https://devfeed.tech/tags/ai.md>), [ai-development](<https://devfeed.tech/tags/ai-development.md>), [claude](<https://devfeed.tech/tags/claude.md>), [claude-code](<https://devfeed.tech/tags/claude-code.md>), [code-review](<https://devfeed.tech/tags/code-review.md>), [command-line](<https://devfeed.tech/tags/command-line.md>), [ruby](<https://devfeed.tech/tags/ruby.md>), [shopify](<https://devfeed.tech/tags/shopify.md>), [tdd](<https://devfeed.tech/tags/tdd.md>), [tools](<https://devfeed.tech/tags/tools.md>), [upgrade](<https://devfeed.tech/tags/upgrade.md>)

### AI overview

This tutorial explains how to use claude-swarm to coordinate multiple Claude Code instances for upgrading Ruby projects. It covers installing the claude_swarm gem, initializing a swarm configuration, and defining a Ruby upgrade workflow with Bash access.

### Source excerpt

One of my colleagues wrote a pretty awesome tool called claude-swarm that orchestrates multiple Claude Code instances as a collaborative AI development team. At Shopify, we are attempting to use it to generate Ruby unit tests at some scale with an army of AI test agents (think a "Ruby Expert" paired with a "TDD Practitioner" and a "Code Review Nitpicker"). But for the purposes of this post, let's just upgrade Ruby in a few projects. First, ensure that you have a working version of command-line Claude code with a monthly subscription, since you will be having a lot of tokens for breakfast. $ claude "Say hello." ╭───────────────────────────────────────────────────╮ │ ✻ Welcome to Claude Code! │ ╰───────────────────────────────────────────────────╯ > Say hello. ⏺ Hello! I'm Claude Code, ready to help you with your software engineering tasks. What can I assist you with today? Install the claude_swarm gem. Note that the gem name has an _. $ gem install claude_swarm $ claude-swarm --help Commands: claude-swarm clean # Remove stale session symlinks claude-swarm generate # Launch Claude to help generate a swarm configuration interactively claude-swarm help [COMMAND] # Describe available commands or one specific command claude-swarm init # Initialize a new claude-swarm.yml configuration file ... Create a claude-swarm.yml. version: 1 swarm: name: "Ruby Upgrade Swarm" main: lead_developer before: - "echo 'Getting started ...'" instances: lead_developer: description: "Lead developer to coordinate Ruby upgrades." directory: .. model: opus allowed_tools: - Bash prompt: | Find the Ruby projects in this directory. Do not recurse further than 3 levels deep. For each project, identify the Ruby version used. Print the project name next to the ruby version only. For the first 5 projects with a Ruby version older than 3.5.5 use ruby_upgrader to upgrade the Ruby version. connections: [ruby_upgrader] ruby_upgrader: description: "Upgrades a Ruby project." model: opus allowed_tools: - Bash