AI coding tools can produce code quickly, but speed alone does not address the hardest software engineering problems. Developers still have to supply context, validate results, preserve maintainability, and stay accountable for what ships. Three Java Champions, each with more than a decade of Java experience, shared concrete examples of using GitHub Copilot in production and open source work, along with the practices they rely on to turn generated output into reliable software.

Vlad Mihalcea

GitHub profile: @vladmihalcea

How has GitHub Copilot helped you recently?

I’ve been using it to analyze, design, and implement various features in both Hypersistence Utils and Hypersistence Optimizer.

For example, I’ve been wanting for a long time to develop a web application that provides Optimizer event history so we can see how performance improvement efforts are materializing in application performance. Before AI, I kept postponing this task because it would have taken me weeks to develop. But using Copilot is like having a new team member, so I used it to create an app that will ship in the next version of Hypersistence Optimizer. Instead of weeks, this task took just two days to implement.

What’s your typical usage? Are you using GitHub Copilot as an IDE plugin or the GitHub Copilot app?

I use GitHub Copilot in IntelliJ IDEA because it’s convenient to run while reviewing source code or during a debug session.

It’s very useful as a code assistant, and it helps me speed up both investigations and building prototypes for features that need validation.

I still review the plans and the code that is generated by AI since their goal is to improve my productivity, not replace me entirely. While AI provides the right answer in many situations, there are still times when the response is not satisfactory and requires my intervention.

Can you share some tips and best practices? For example, which skills are you using?

I created my own skills so that whatever text Copilot generates looks like something I wrote by hand.

This skill captures my voice from 65 of my published articles, so a draft comes back sounding like my blog instead of like a language model. This is really useful when generating documentation for this project, since it lets the LLM learn from 12 years of blogging and provide examples that follow the same principles, such as expressiveness, testability, and simplicity.

Throughout my career as a software engineer, I’ve learned that we spend far more time on code maintenance than on creating it. Therefore, because maintenance and stability are critical for existing products, I developed skills that help AI mimic existing code practices in accordance with the principle of least astonishment.

One tip is to change the model type based on the type of operation. For thorough thinking, I use Opus with the 1 million context window and Extra High or Max reasoning. However, for simpler tasks, such as generating reports, release notes, or verifying documentation, I use lighter models that consume fewer tokens.

Patrick Baumgartner: from TDD coach to agent orchestrator

Baumgartner's coaching work runs alongside normal business operations, so change happens incrementally: teams attack immediate problems together, define a destination and a way to measure improvement, then proceed step by step. The toolkit varies by situation — Software Craft practices such as TDD, refactoring, continuous delivery and XP; architecture and modularisation with Spring Boot and Spring Modulith; Spring Security; container optimisation for Java and Spring Boot applications; and increasingly, agentic engineering. He describes working with a team rather than making it dependent on him, aiming for the point where he is only needed as a sparring partner. That preference, he suggests, is why the current generation of AI tools appeals to him: they add another powerful tool for learning, experimenting and solving problems with people.

Three experiments that shifted his view of Copilot

Migrating 900 slides. Baumgartner moved roughly 900 teaching slides across 15 presentations from Reveal.js to Marp. The decks were not simple: many carry code, animations and live-editable JavaScript examples, and their flow had already been validated with students. Done by hand, the work would likely have taken a week or more — and just as likely been postponed indefinitely. With Copilot he analysed the existing material, defined the target, wrote a migration plan, set constraints, supplied examples and specified what had to remain intact, then worked from structural changes down to details.

Verification carried the project. Screenshots of the Reveal.js and Marp versions were compared visually and algorithmically, a small application ran both decks side by side for manual animation checks, and the generated PDFs were compared too. The agent could not simply declare the job finished; there was evidence. The migration took roughly eight to ten hours, with Baumgartner retaining ownership of content, teaching intent and every meaningful change. Copilot might fix a typo on its own, but altered content, versions, dates or meaning were discussed. His summary principle: delegate execution, not responsibility.

A Windows-only bug. A terminal UI application built with Tamboui worked perfectly on Linux under WSL — until a real user ran it in Windows CMD or PowerShell and found the Enter key ignored in some forms. Copilot turned the feedback into reproducible UI tests: on Linux it navigated the application with tmux, and through that work Baumgartner learned about psmux, which allowed similar flows in PowerShell. The problem was reproduced, a local fix written, the root cause traced into Tamboui, and the upstream fix merged.

The test descriptions eventually outgrew the available context, so he asked Copilot to redesign how the tests were represented. Instead of verbose tables of identifiers, names and repeated descriptions, it proposed a compact arrow notation — step → step → input — with extra information only where critical. What struck him was that the collaboration was not about executing information but about how information should be represented for execution: not maximally human-friendly, but semantically faithful and efficient for the agent.

A maths learning app. Asked to help someone practise mathematics, Baumgartner built an app covering addition, subtraction, multiplication, division and division with remainder for grades one through four, rather than another simple quiz. With Copilot he researched spaced repetition, immediate feedback, adaptive difficulty, motivation, cooperative learning and gamification without dark patterns. The research challenged his original assumption of a generic, universal app for children; the actual school curriculum differs enough in topic order, operations and number ranges that "universal" was unnecessary. He scoped it to Switzerland first, which — given the country's languages — means German, French, Italian and English. The app includes cooperative and competitive two-player modes, explains one way to solve a question when an answer is wrong, and brings similar questions back through spaced repetition.

The architecture deliberately has no backend: there is no reason to collect children's personal data on a server, so data stays on the device and the app is hosted on GitHub Pages. Started as a small experiment, it is now used and shared among friends. The lesson: use Copilot not only to implement a solution but as a sparring partner to expand and challenge the solution space before building.

How he works day to day

The classic autocomplete experience is gone from his workflow, which has moved almost entirely to agentic setups. VS Code serves when he needs a tight visual feedback loop over documents, code, presentations or intermediate results; the terminal coding agent suits larger execution-oriented tasks he can delegate and then inspect as a complete result. The interface follows the feedback loop required.

One experiment stands out: steering the GitHub coding-agent workflow from his phone on a roughly four-hour train journey to JavaLand, exploring a Maven plugin that optimises which Spring Boot auto-configurations are loaded. He gave tasks, reviewed results, decided the next step and sent the next instruction. By arrival he had a working Maven plugin, a CI pipeline, performance tests against a sample application, and reports demonstrating whether the optimisation worked. He was not writing Java on a phone — he was reviewing, deciding and steering while the agent executed.

MCP servers get used when they add capability or context: Context7 for documentation, integrations with his Logseq knowledge base, and experiments with an MCP integration for Adobe Premiere Pro. But he does not use MCP for its own sake; where a good CLI exists he often prefers it as more direct and faster. Nor does he model the agent as a junior developer — more a sparring partner, which, like anyone joining a project, first needs bringing up to speed. Context is king.

His four working principles

  1. Stop one-shot prompting. For anything non-trivial he wants the problem understood before execution. He often asks the agent to interview him first, to surface implicit assumptions and expectations. A good agent executes the wrong idea incredibly quickly, which does not make the detour cheaper. On his Spring Boot auto-configuration optimiser he initially supported both Spring Boot 3.5 and 4.x; architectural differences meant implementing both approaches, and removing the 3.5 path later was painful. Detours are expensive — get the requirements right before accelerating. Once intent is clear he reviews the plan on four points: are assumptions visible, are verification gates defined, does the approach fit the architecture, and does it avoid unnecessary work?
  2. Define task-specific verification gates. "Done" must not mean the model says it is done. A refactoring might require passing tests, proof that all intended files were handled, updated documentation and no leftover TODOs; the slide migration required screenshots, visual diffs, animations, PDF output and content completeness. The gates vary by task; the principle does not. He points to a skill called unlazy that follows the same idea: create explicit gates, work through them deliberately, prove completion rather than take shortcuts.
  3. Turn lessons into reusable skills. He writes and maintains Agent Skills, updating them whenever an execution teaches something — a misunderstood convention, a missing check, a better approach — so the lesson is present next time. One example, maintainer-audit, inspired by the open agent skills community, runs across his repositories and checks and where appropriate fixes what a maintained project should have: repository documentation, CI pipelines, Dependabot configuration, CodeQL, consistent cleanup and other project standards. The goal is not another audit report to read but automatic return to baseline. He treats skills as living engineering knowledge rather than static prompts.
  4. Surround the probabilistic model with deterministic tools. A personal quality harness wraps Checkstyle, PMD and Spring Modulith architecture checks into a consolidated report the agent can consume, runnable from hooks so the agent can iterate: change → execute checks → read findings → fix → execute again, until no findings remain. That does not replace his own inspection — he still runs the application, interacts with it and judges behaviour and appearance. He also implements feature by feature on purpose for the tighter feedback loop, having learned that asking for everything at once produces large, generic implementations that are harder to clean up than smaller increments built correctly.

More broadly, he argues developers should discuss how they use these tools — sharing experiments that worked and those that didn't — and adapt ideas to their own context instead of seeking one universal AI development process. The sparring-partner framing extends to Friday nights, when no human pair is available and something can challenge an idea or act as a sophisticated rubber duck. The ideas still go back to real people: human feedback, product understanding and real-world context remain essential.

The profession's shift

Software engineering students, in his view, should not aspire primarily to become code-producing machines but strong problem solvers — people who understand users and stakeholders, find the real problem behind a requirement, challenge assumptions, grasp products and markets, make architectural trade-offs, think critically, work in a structured way and transfer solutions between contexts. The model is extraordinarily good at generating possibilities and executing; the human usually knows far more actual context, including what nobody ever documented. He wants to stay in charge of shaping the solution while the agent takes over more of the execution — which, as he puts it, means AI is putting the engineering back into software engineering.

Frank Delporte

GitHub profile: @FDelporte

Background

Delporte traces his interest in computing back to a Commodore 64 he received at age 11. He spent time after school in video production and television work before returning to coding, driven by the need to put video on DVD and build the first websites.

The fifth-button bug in Pi4J V4

Delporte describes Copilot as a good companion for evaluating pull requests and digging into bug reports. A concrete example came from Pi4J, the library for building Java applications that interact with electronic components on the Raspberry Pi. Version 4, released in February 2026, introduced a plugin built on the Foreign Function & Memory API. Despite thorough pre-release testing, a report arrived shortly after launch: with five input buttons configured, pressing any of the first four triggered code execution, while the fifth did nothing.

Nothing was visibly broken — no exception, no log warning, just a silent fifth button. The older gpiod plugin ran the identical hardware and application without issue, which pointed at the new FFM code sitting on top of the Linux kernel's GPIO character device and communicating through native calls.

Delporte assigned the issue to the Copilot coding agent, supplying two file paths as a starting point: the provider class and FFMDigitalInput. Roughly half an hour later a pull request appeared with an explanation he had not anticipated.

Each input runs an event watcher that blocks in a native poll() call waiting for the kernel to report a change on its GPIO line. Those watchers ran in virtual threads, and a virtual thread blocking inside a native call cannot be unmounted from its carrier thread — it stays pinned for the entire duration of the call. A Raspberry Pi 4 has four cores, so the virtual thread scheduler has four carrier threads. Register four inputs and all four carriers remain parked in poll() indefinitely; the fifth watcher is created but never scheduled. That matches the report precisely: not flaky events, but a fifth input that does nothing.

What Delporte valued most was the review conversation rather than the diff. He asked Copilot to elaborate:

I thought the goal of virtual threads was exactly to step away from the platform thread limit? So how is this a problem that four virtual threads block the four threads on a four-core processor like the Raspberry Pi?

In the pull request, Copilot connected the four-core Raspberry Pi to the reported symptom and proposed a simple fix along with further improvements found during its investigation.

Practices that work

  • Point it at the right files: Two file paths added to issue #622 saved a large amount of searching. Developers usually have a hunch about the area even without knowing the bug, so hand that to the tool instead of making it scan the whole codebase.
  • Demand proof and tests: Delporte frequently asks for a unit test validating the change; it is now a rule in his instructions file. It also catches incorrect diagnoses, since a test written for a wrong fix will not fail on the old code.
  • Ask why: When a fix is unclear, ask for an explanation. Treat that explanation as a deliverable and record it in project documentation, the ticket or the pull request so later readers learn from it — in this case it prompted further questions within the Pi4J team about virtual thread pinning on native calls.
  • Keep humans accountable: Every Pi4J pull request was reviewed and approved by maintainers before merging. The working model is agent investigation with human responsibility for what ships. Delporte reviews generated code in his other projects too, because tools often duplicate methods or recreate functionality that exists elsewhere. AI-assisted coding is fast, and it can create a mess just as fast; readable, maintainable code remains the developer's responsibility for colleagues and future-self alike.

Get started with GitHub Copilot >