DEV Community

Rost Khaniukov
Rost Khaniukov

Posted on • Edited on

Transforming DevEx Through Bulk Refactoring (and How AI Can Assist)

Developer Experience today is not just a trend but a real way of boosting efficiency in tech companies. A suitable set of tools, well-established processes, and a supportive environment can transform an ordinary workday into a productive and inspiring one. What’s better: spending a whole day on the creative process and developing interesting solutions or getting stuck in monotonous configuration updates for dozens of repositories? The answer is obvious. When a developer is satisfied, they not only perform their job better but also generate new ideas that drive the company's progress.

I have mentioned dozens of repositories not accidentally — it’s my sore point. Throughout my career, I’ve repeatedly faced situations where I had to make the same type of changes across a huge number of repositories. With each such task, work that was supposed to be creative turned into something mechanical: cloning the repository, creating a branch, making changes, committing, pushing, submitting a pull request, and repeating endlessly. Of course, we’re not the only ones who’ve struggled with this. Spotify team, for instance, shared their experience in a blog post, explaining how they handled updating the Log4j version across all their projects after the notorious security issue.

Sooner or later, after a series of repetitive tasks, every developer eventually thinks: "Why not automate this process?". The idea is simple but effective — to write a CLI tool that automates the manual work across multiple repositories: creating branches, modifying files, pushing them to GitHub, and creating Pull Requests using the GitHub API. Building such a tool itself can be more interesting than making manual changes, and on top of that, we won't have to waste time on manual tasks since we'll automate them. Such an approach temporarily enhances the Developer Experience, but soon you’ll realize it’s not enough. Time will pass, and you'd like to move to the next level. Instead of writing separate scripts for each task, it becomes easier to describe everything declaratively, for instance, in YAML. Something like this:

definition:
  to: update-license
  commit-message: "Update license"
  if:
    - condition: json
      path: package.json
      op: jq
      query: .license | startswith("UNLICENSED") | not

  steps:
    - do:
        - action: take-file
          from: package.json
        - action: replace
          regex: true
          what: '"license":\s+"(.*)"'
          with: '"license": "UNLICENSED"'
Enter fullscreen mode Exit fullscreen mode

To begin with, parameters to and commit-message are simply names for the branch and commit so that we won't dwell on them. Let’s move on to something more interesting: the if and steps blocks. The if block allows us to define which specific repositories need changes. After all, updating all 10, 100, or 1000 repositories in the organization is not always necessary. In our case, the filter selects only those repositories where the package.json file contains the license attribute that doesn’t start with UNLICENSED. In the steps block, we describe the specific actions: we take the file and replace the license attribute with UNLICENSED. It’s simple but effective.

Creating such a configuration and running a CLI tool to apply it takes much less time than manually updating even ten repositories. And when we're talking about hundreds of repositories, the need for such a tool becomes clear — it's an obvious requirement for any team.

The next logical step would be to expand the tool's capabilities: add more ways to filter repositories and provide more options for manipulating files (since regular expressions aren't a silver bullet, moreover, not every developer can easily handle them — and we want to make sure they don’t have to struggle with them, right?). But frankly, diving into these details here would be too boring.

So, let’s wrap up with something more spectacular, like in the finale of my favorite childhood show, MythBusters — a real explosion! Let’s throw the YAML manifest out of the script. If writing it is so simple that even a trainee can handle it, then why not delegate this task to AI?

To begin with, we’ll train the AI to create manifests, and then it will be able to generate them independently, receiving only a description of the task from the developer. Since the AI configuration part is always the same, we’ll hide it under the hood to avoid bothering developers with unnecessary details. In the end, the generated manifest will be passed to our CLI tool — and it works!

To finish, here’s a video showing a PoC demo.

Top comments (0)