DEV Community

Cover image for The Refactor I Did Not Do
Remus Lazar
Remus Lazar

Posted on Originally published at Medium

The Refactor I Did Not Do

I have been building a media display for my living room. An old screen, a Raspberry Pi 4, and UxPlay, so anything in the house can send video to it over AirPlay. A side project, the kind you do in the evenings because you want the thing to exist.

It mostly worked. What did not work was YouTube.

The Pi would pick a 1440p VP9 variant, fall back to software decoding, and consume roughly 2.7 of its four CPU cores. The 1080p H.264 variant of the same stream ran on the hardware decoder at 11%. Nothing in the pipeline could express the one thing I knew and it did not: this device should stay at 1080p.

GStreamer's DASH demuxer has properties for maximum video width and height. The HLS demuxer has neither. So I opened an issue.

The file I read before writing any code

A maintainer replied, the scope looked reasonable, and I set the project up in VS Code — in Docker, with the test suite wired in, the way I like to work. Then I read AGENTS.md, the file where the project tells coding agents what they may and may not do.

It opens by saying that GStreamer depends on the effort of human reviewers, and calls that effort a scarce resource. Everything after it follows from that sentence.

AI-written merge request descriptions and commit messages: banned. AI-written issue text: banned. AI-generated replies to reviewers: banned. For descriptions and commit messages, the policy cites wasted reviewer and triager time. For replies, it says generated responses undermine the human interaction that code review depends on. On the code itself the wording is softer but points the same way: the agent is to provide guidance rather than solutions, and to let the human formulate the approach. The file also instructs the agent to refuse me if I push.

So I had two honest options. Disagree with the policy and say so in the issue, and not contribute. Or accept it and work the way it asks.

There was not a third option, at least not for me: do as I liked and let the output speak for itself. Not because I am especially principled — it is a small patch to a project I wanted something from. But the rules are the terms the project offers, and I would rather turn the terms down than take them selectively. If I could not follow them for a two-property patch, I would not have been a contributor. I would have been a user with a keyboard.

What the rules actually left me

More than I expected.

My C is rusty. The last time I wrote any seriously it was for an ESP32, years ago. Getting a modern GStreamer checkout building in a container, with the right meson invocation and the unit tests running, is exactly the kind of work where an agent earns its keep — and none of it is prohibited. Nor is asking it to walk me through the variant selection path until I could explain it back.

That took a short while, and then I was at home again. Editor where I wanted it, tests one command away, cursor in the right file on the right line.

That was when the rules started to matter, though not in the way I expected.

The refactor I left out

In the issue, I had raised a question about scope. These two properties already exist on the DASH demuxer. Should the HLS one get its own copy, or should they be promoted to the shared base class so all three demuxers have them? I wrote that promoting looked tidier, that I had no preference, and that I would follow the maintainer's.

He said it was fair to check, as long as it did not multiply the complexity.

Then I sat down to write it, and I did not do it.

Not because I had concluded it was wrong. I still do not know whether it would have been better. It might have paid for itself across three demuxers, or it might have dragged public properties onto a base class and raised an awkward question about the third one that nobody wanted to answer. What I know is that the decision felt completely different when the hours were mine. My evenings are finite, the two-property version solved my problem, and I chose the small one.

Proposing it was free. Building it was not. I had not noticed how much that gap was deciding for me.

Here is the part I keep turning over. If I had been running this the way I run everything else, I would have asked the agent, and it would have argued the case, and it would have been a decent case, and I would have said go. The merge request would have been larger. It would have touched code paths my problem never touched. And the extra scope would have cost me almost nothing.

It would have cost the reviewer, whose day is exactly as long as it was before any of this arrived. A base-class move across three demuxers is a different review from two properties and a guard clause. That is the sentence at the top of their policy file, arriving from the other direction: the thing being spent is not my attention.

I do not think I was being careless before. I think the price signal was missing.

The commit messages

Three commits went into the merge request. I wrote all three messages by hand, which I had not done in about six months.

It showed, though not in the content. I know what a commit message needs to say. What was gone was the fluency — the sense of whether a line sounds right, whether to polish it or leave it, the small certainty that used to make this a thirty-second job. I sat there rereading my own subject lines, unsure.

Being the kind of person who checks rather than wonders, I went back and read my commit messages from before all this. Two years of them.

They are not as good as what the model writes for me now. They are less consistent, occasionally sloppy, sometimes too long. They also have a voice. You can tell which ones I wrote at the end of a hard day and which ones came easily. There is a warmth in them that I do not find in the generated ones.

Correct and empty is a trade I have made before without noticing.

I wrote the merge request description by hand too. It came out compact. No headings, no tables, no bold text, four short paragraphs: what it does, where the limits apply, which two pre-existing bugs I kept as separate commits and why, and an offer to split them out.

I have been arguing for exactly that since the first article in this series. It took someone else's ban to get me to do it.

What the slow version found

It was slower. I want to be honest that it was slower and that I noticed.

It also found two bugs that were not mine. A NULL dereference when no initial variant is selected. And an ordering mistake in meson.build that had been quietly clobbering a dependency, with the result that a set of tests had not run since March 2022 — including, it turned out, the ones I was about to add.

I cannot prove the pace found those. Someone working fast might have hit the same wall, because the second one blocked my own tests from running and was therefore unavoidable. But I was reading that file rather than generating it, and I have a suspicion about which mode notices a variable being overwritten thirty lines later.

The question I could not close

In the last article I worried out loud that the skill I use to judge AI-written code depends on continuing to write it myself. I proposed an experiment: write something by hand now and then, not for productivity, but to find out whether the fluency is still there.

I did not run it. I am fairly sure I was never going to run it, because nothing would have made me. There is always something more useful to do with an evening than deliberately taking the slow road.

Then a stranger's policy file made me.

The finding is smaller and more specific than the worry. I could still reason about this patch and explain the choices I had made. The clearest loss of fluency showed up in the commit messages: I knew what to say, but writing it no longer felt automatic. I had not lost the ability, only the ease. That does not settle the larger question about my coding skills.

The constraint also left me with a smaller change to ask someone to review, and plainer prose in my own voice. I still cannot say whether the refactor would have been better engineering.

The constraint did what my own experiment had not.

I am not going to pretend this generalises into a practice. I am not about to hand-write every commit message at work, and I would not recommend it to my team. But I think the open source projects are going to be where this gets worked out first, and not because they are conservative. Because maintainers have limited time and no obligation to review my patch. Any extra scope I propose is a request for more of their attention.

The rest of us are spending the same resource. We just have not been sent the bill yet.

All of this is public, if you want to check the work: the issue, the policy file, and the merge request with the three commit messages I wrote by hand. One of the two fixes has since been merged. The patch itself is still waiting for a reviewer.


Originally published on Medium.

Top comments (0)