Prepare for Maven interview questions grouped by experience level.
Maven Interview Question & Answers
0-2 Years
Maven is a build automation and project management tool for Java projects that handles compiling code, managing dependencies, running tests, and packaging the final build artifact. It's built around a standardized project structure and a declarative configuration file rather than requiring a custom build script for every project.
The POM, or Project Object Model, is Maven's core configuration file, named pom.xml, that describes a project's dependencies, plugins, build settings, and metadata like its group ID, artifact ID, and version. Maven reads this file to understand exactly how to build and manage the project.
A dependency is an external library or module a project relies on, declared in the POM file with its group ID, artifact ID, and version so Maven knows exactly which version to download and include. Maven automatically downloads dependencies and their own dependencies from a repository rather than requiring them to be manually added to the project.
A Maven repository is a storage location for build artifacts and their metadata, either local on a developer's machine or remote, like Maven Central, where Maven fetches dependencies that aren't already available locally. Maven checks the local repository first before reaching out to a remote repository to download something it doesn't already have.
The local repository is a folder on a developer's machine, by default located at ~/.m2/repository, where Maven caches downloaded dependencies and plugins so they don't need to be re-downloaded on every build. It also stores artifacts built locally, like a project's own JAR file after a successful build.
Maven Central is the default and most widely used public remote repository that hosts a huge number of open-source Java libraries, which Maven can automatically download dependencies from when they're declared in a POM file. Most Java projects rely on it as their primary source for common third-party libraries.
An artifact is uniquely identified by its group ID, which is typically the organization or project's reverse domain name, its artifact ID, which is the specific project or module name, and its version. Together these three coordinates let Maven and any project depending on the artifact reference exactly the right version.
The Maven build lifecycle is a predefined sequence of phases, like validate, compile, test, package, and install, that a build progresses through in order. Running a specific phase automatically runs every phase before it in the sequence as well.
The clean command removes the target directory, which holds compiled classes and other build output from previous builds, giving you a fresh starting point. It's commonly run before a build to make sure old, possibly stale build artifacts don't interfere with the new one.
The compile phase compiles the project's main source code, converting Java source files into class files, without yet running tests or packaging anything. It comes before the test and package phases in the standard build lifecycle.
The package phase takes the compiled code and packages it into its distributable format, like a JAR or WAR file, as defined by the project's packaging type in the POM. This happens after compiling and testing in the standard lifecycle sequence.
The install phase copies the packaged artifact into the local repository, making it available as a dependency for other projects being built locally on the same machine. It runs after package in the standard lifecycle sequence.
Install copies the built artifact into the local repository on the developer's own machine, while deploy copies it to a remote repository so other developers or systems can access it. Deploy is typically used when publishing a finished, shareable version of a library or application.
A Maven plugin is a piece of reusable functionality that performs a specific task during the build, like compiling code, running tests, or creating a JAR file. Most of Maven's actual build behavior comes from plugins bound to lifecycle phases rather than from Maven's core alone.
A dependency is a library your project's code actually uses at compile time or runtime, while a plugin is a tool that performs a task as part of the build process itself, like generating code or running static analysis. Both are declared in the POM but serve fundamentally different purposes.
Dependency scope controls when and where a dependency is available during the build and at runtime. The default compile scope means the dependency is available in all phases and included in the final packaged artifact, unlike more limited scopes like test or provided.
A dependency with test scope, like a testing framework such as JUnit, is only available during the test compilation and execution phases and isn't included in the final packaged artifact. This keeps testing-only libraries out of the production build.
Provided scope means the dependency is needed to compile the code, but it's expected to already be available in the runtime environment, like a servlet API provided by an application server, so it isn't bundled into the final packaged artifact. This avoids including a dependency that would conflict with or duplicate one already provided by the deployment environment.
A parent POM is a POM file that other project POMs inherit configuration from, letting shared settings like dependency versions, plugin configuration, and properties be defined once and reused across multiple related projects. A child project declares the parent in its own POM and inherits its configuration automatically.
An archetype is a project templating mechanism that generates a new project with a predefined directory structure and starter files, saving you from manually setting up the standard layout by hand. Running mvn archetype:generate with a chosen archetype scaffolds a new project ready to build on.
By convention, main application source code lives under src/main/java, resources like configuration files live under src/main/resources, test source code lives under src/test/java, and test resources live under src/test/resources. Following this convention means Maven can find everything it needs without additional configuration.
A property is a named value defined in the POM's properties section, like a Java version number or a shared dependency version, that can be referenced elsewhere in the file using the ${propertyName} syntax. It helps avoid repeating the same value in multiple places and makes updating a shared value, like a version number, a single change.
The target directory is where Maven places all build output, like compiled classes and the final packaged JAR or WAR file, generated during a build. It's typically excluded from version control since it's entirely regenerated by running a build.
Transitive dependency resolution means Maven automatically includes the dependencies you directly declare and, beyond just those, the dependencies those dependencies themselves depend on, without you having to declare each one manually. This saves significant manual effort but can also occasionally pull in a version conflict between two different transitive dependencies requiring the same library at different versions.
A SNAPSHOT version, indicated by a -SNAPSHOT suffix like 1.0-SNAPSHOT, represents an in-development version that can change over time, and Maven will check for updated snapshots from a remote repository rather than treating it as a fixed, immutable version. This is different from a release version, which is expected to never change once published.
The test command runs the project's unit tests using the configured testing framework, typically after compiling both the main and test source code. If any test fails, the build stops at this phase by default rather than proceeding to packaging.
settings.xml holds user or machine-specific Maven configuration, like credentials for a private repository or custom repository mirror settings, that shouldn't be checked into a project's own POM since it can vary between developers or environments. It's typically located in the ~/.m2 directory.
A module is a sub-project within a larger multi-module Maven project, each with its own POM, that's built as part of a coordinated overall build managed by a parent POM listing all the modules. This lets a large application be broken into smaller, independently buildable pieces while still being built together as a whole.
The -D flag sets a system property on the command line, which can override a property defined in the POM or control specific plugin behavior, like skipping tests for that particular build run. It's a common way to adjust build behavior temporarily without modifying the POM file itself.
Convention over configuration means Maven assumes a standard project structure and default behavior unless you explicitly configure something different, which significantly reduces how much configuration a typical project needs compared to fully specifying everything manually. Following Maven's conventions, like the standard source directory layout, means most projects need only minimal configuration in the POM.
The effective POM is the fully resolved configuration for a project after Maven merges the project's own POM with any inherited settings from parent POMs and applies default values. Running mvn help:effective-pom shows this complete, merged configuration, which is useful for understanding exactly what settings actually apply after inheritance.
Package produces the built artifact, like a JAR or WAR, while verify runs after package and is used to run additional checks, like integration tests, against that packaged artifact before it's considered ready. Verify exists as a dedicated phase specifically for validation steps that make more sense after packaging than before it.
A build profile is a set of configuration changes that can be conditionally activated, letting a single POM produce different build behavior depending on which profile is active, like a different target environment or dependency set. Profiles are defined in the POM and can be activated explicitly on the command line or automatically based on a condition.
The Surefire Plugin is responsible for running a project's unit tests during the test phase of the build lifecycle and reporting the results. It's one of the most commonly used plugins since nearly every Maven project relies on it, whether explicitly configured or through Maven's default behavior.
In Maven's own terminology, the correct and commonly used term is repository, referring to where artifacts are stored and retrieved from, whether local or remote. The word registry is sometimes used informally or in comparison to other ecosystems, but Maven's documentation and tooling consistently use the term repository.
The Release Plugin automates the process of preparing and performing a project release, including tasks like updating the version number from a SNAPSHOT to a release version, tagging the release in version control, and building and deploying the release artifact. It's designed to make the release process consistent and repeatable rather than relying on a developer manually performing each step correctly every time.
3-6 Years
I'd use mvn dependency:tree to see exactly which dependency is pulling in which version and understand the conflict, then explicitly declare the desired version directly in the project's own POM, since a directly declared dependency takes precedence over a transitively resolved one. I'd also verify the chosen version is actually compatible with both libraries that depend on it, since simply picking one version doesn't guarantee it works correctly with everything that needs it.
I'd create a parent POM with packaging type 'pom' that lists each module and centralizes shared configuration like dependency versions and plugin settings, then create each module as its own sub-project with its own POM inheriting from that parent. I'd design the module dependencies deliberately, like having the web layer depend on the core library rather than the reverse, to keep a clean, logical dependency direction across the project.
I'd use the command line flag mvn install -DskipTests, which skips test execution for that specific run without modifying the POM, rather than permanently disabling tests in the configuration which risks someone forgetting to re-enable them. For skipping tests more thoroughly, including not even compiling them, I'd use -Dmaven.test.skip=true instead, though I'd be cautious about defaulting to that broader option regularly.
I'd first double check the group ID, artifact ID, and version are spelled and formatted exactly correctly in the POM, since even a small typo results in a not-found error rather than a clearer typo warning. I'd also check whether the dependency is hosted somewhere other than Maven Central, requiring an additional repository to be configured in the POM or settings.xml, since Maven won't automatically search repositories that aren't explicitly declared.
I'd add the internal repository's URL to the POM's repositories section, or more commonly to settings.xml so it's not duplicated across every project, along with the necessary authentication credentials for accessing it. I'd keep credentials out of the POM itself and rely on settings.xml or environment-specific configuration, since the POM is typically checked into version control and shouldn't contain secrets.
I'd configure the maven.compiler.source and maven.compiler.target properties, or more simply the maven.compiler.release property in newer Maven and plugin versions, in the POM's properties section, which the Compiler Plugin then uses when compiling the project's source code. Using the newer release property is generally preferable since it enforces consistency between source and target more strictly than setting them separately.
I'd use the -T flag to enable parallel builds across multiple modules in a multi-module project, since Maven can build independent modules concurrently rather than strictly sequentially by default. I'd also check whether unnecessary plugins are running on every build, like a heavy static analysis tool that could instead run only in CI rather than on every local developer build.
I'd use the exclusions element within the dependency declaration in the POM, specifying the group ID and artifact ID of the transitive dependency to exclude, which prevents Maven from pulling it in through that particular dependency path. I'd document why the exclusion exists with a comment, since an exclusion without context can be confusing for someone maintaining the project later who doesn't know the original reasoning.
I'd use the Maven Shade Plugin or the Assembly Plugin configured with the jar-with-dependencies descriptor, both of which package the project's compiled classes along with the classes from all its dependencies into a single, self-contained JAR. I'd choose based on the specific needs, since Shade offers more control over things like relocating conflicting package names, which Assembly doesn't handle as gracefully.
I'd use the Maven Wrapper, which checks a specific Maven version into the project so everyone runs the same version through the mvnw script rather than whatever version happens to be installed locally. For Java version consistency, I'd rely on the compiler plugin's configured target version to at least catch compatibility issues at compile time, and encourage the team to standardize on a common Java version through documentation or a tool like a version manager.
I'd check whether the CI environment has access to the same repositories the local build relies on, since a common cause is the local repository having a cached copy of a dependency that the CI environment, starting from a clean cache, can't reach due to a missing repository configuration or network restriction. I'd also verify the CI pipeline is using the same Maven and Java versions as local development, since a version mismatch can sometimes surface as a confusing dependency-related error rather than an obviously version-related one.
I'd add the JaCoCo plugin configured to instrument the code during test execution and generate a coverage report, bound to an appropriate phase like verify or test, and configure it to fail the build if coverage falls below a defined threshold if the team wants that enforced. I'd integrate the generated report with whatever CI or code quality dashboard the team already uses so coverage trends are visible without someone manually checking a local report.
I'd declare the shared dependency versions in the parent POM's dependencyManagement section rather than each module's own POM, so individual modules reference the dependency without specifying a version, inheriting it consistently from the parent. This avoids the situation where different modules accidentally end up using different versions of the same library due to independently specified versions.
I'd use the Failsafe plugin bound to the integration-test and verify phases for integration tests, keeping unit tests running through the standard Surefire plugin bound to the test phase, so the two are clearly separated and integration tests don't run as part of every quick local build. I'd typically name integration test classes with a distinct suffix, like IT, so Failsafe's default inclusion pattern picks them up correctly without needing extensive additional configuration.
I'd check for differences in the local repository cache first, since a stale or corrupted cached artifact on one machine can cause a build to behave differently even with an identical POM, and try clearing the relevant cached dependency to force a fresh download. I'd also check for any SNAPSHOT dependencies in the project, since those can resolve to different actual versions on different machines or at different times depending on when each machine last fetched the latest snapshot.
I'd move shared plugin configuration into the parent POM's pluginManagement section so individual modules inherit consistent settings without repeating full configuration blocks, only overriding specifics where a module genuinely needs different behavior. I'd also periodically review whether accumulated plugin configuration is still actually needed, since POM files tend to accumulate settings from past requirements that are never removed even after they're no longer relevant.
I'd use Maven profiles, defined in the POM and activated either explicitly with a flag or automatically based on a condition, to swap in environment-specific configuration like a different properties file or dependency scope. I'd keep the differences between profiles minimal and clearly documented, since heavily divergent profile configurations can make it hard to reason about what a given build actually produces.
I'd use a tool like the Versions Plugin to identify which dependencies have newer versions available, then upgrade incrementally rather than jumping every dependency to its latest version simultaneously, testing thoroughly after each meaningful batch of changes. I'd prioritize dependencies with known security vulnerabilities first, since those carry a clearer, time-sensitive justification compared to purely routine version currency.
I'd use the Enforcer Plugin, which can validate conditions like a required property being present before the build proceeds, giving a clear, early failure message rather than letting the build continue and fail more confusingly at a later phase. This is particularly useful for catching a missing configuration value before it causes a harder-to-diagnose failure deep into the build or test execution.
I'd document the key custom aspects that aren't obvious from just reading the POM, like why a particular profile exists or what a custom plugin configuration is actually for, since the standard Maven lifecycle itself doesn't need much explanation for anyone already familiar with Maven generally. I'd keep this documentation close to the code, like in a README, and treat updating it as part of making any significant build configuration change, rather than letting it drift out of sync over time.
I'd find a plugin that already provides the specific code generation capability needed rather than writing custom logic from scratch, and bind its execution to an appropriate lifecycle phase, typically generate-sources, so the generated code is available before the compile phase runs. I'd configure the plugin's output directory to be picked up automatically as a source folder so the generated code compiles alongside the rest of the project without extra manual steps.
I'd add the classifier element within the dependency declaration in the POM, specifying the exact variant needed alongside the normal group ID, artifact ID, and version. This is common for things like needing the sources JAR for documentation purposes or a platform-specific native library variant of a dependency.
I'd use a plugin like the Maven Dependency Plugin's lock-related goals or generate and commit a resolved dependency list that a build validation step checks against, so an unexpected transitive dependency version change is caught rather than silently affecting the build. I'd treat this as particularly important for release builds specifically, where reproducibility matters most, even if it's less critical for everyday local development builds.
I'd add a plugin like OWASP Dependency-Check bound to an appropriate phase, configured to fail the build or at least generate a clear report when a dependency with a known vulnerability above a defined severity threshold is detected. I'd integrate this into the CI pipeline rather than relying purely on developers running it manually, since a check that depends on someone remembering to run it tends to get skipped under delivery pressure.
6-8 Years
I'd establish a company-wide parent POM or a bill-of-materials POM that centralizes approved dependency versions through dependencyManagement, which individual project teams inherit from rather than independently choosing versions project by project. I'd pair this with a clear, low-friction process for teams to request a version update or a new approved library, so the centralization doesn't become a bottleneck that teams route around by ignoring it.
I'd evaluate whether the module boundaries genuinely reflect independent, loosely coupled pieces of the system, since a build that's slow specifically because of unnecessary coupling between modules often benefits more from restructuring than from just adding more build parallelism. I'd also look at incremental build tooling and build caching options, and consider whether some modules could be built and versioned independently rather than always as part of one large coordinated multi-module build.
I'd centralize commonly used plugin configuration, like the compiler plugin's target Java version or shared static analysis rules, into an organization-wide parent POM or a set of reusable configuration snippets teams can inherit, rather than letting each team configure the same plugins independently and inconsistently. I'd balance this centralization against genuine flexibility for teams with legitimately different needs, avoiding an overly rigid mandate that doesn't account for real differences between project types.
I'd audit the POM for signs of accumulated cruft, like unused dependencies, redundant plugin configuration, or overly complex profile logic that's grown organically over years without anyone stepping back to simplify it. I'd weigh the cleanup effort against the actual pain it's causing the team, like slow or unreliable builds, rather than pursuing cleanup purely for its own sake if the current configuration, while messy, isn't genuinely blocking the team's productivity.
I'd generally discourage relying on SNAPSHOT dependencies across team or module boundaries in anything beyond active, coordinated development, since a snapshot resolving to a different actual build at different times can introduce non-reproducible build behavior that's hard to debug. I'd push for a disciplined release process where teams consume properly versioned releases of each other's shared libraries instead, reserving snapshots for genuinely temporary, tightly coordinated cross-team development windows.
I'd design the pipeline to run fast checks, like unit tests and lightweight static analysis, on every commit or pull request for quick feedback, while reserving slower checks, like full integration test suites or a heavier security scan, for a less frequent stage, like right before a merge to the main branch or on a scheduled basis. I'd make quality gate failures clear and actionable in the pipeline output, since a vague failure message tends to erode developer trust in the gate over time.
I'd check for dependency version mismatches that aren't obvious from the POM alone, like a different transitive dependency resolving differently due to a slightly different local repository state, and compare the actual effective POM and dependency tree between environments rather than assuming the declared versions guarantee identical resolution. I'd also check whether any environment-specific profile or system property is silently changing build behavior between the two environments.
I'd weigh the genuine limitations the team is actually hitting with Maven against the significant migration cost and risk of moving a portfolio of working projects to a different tool, since a tool migration of this scale rarely pays for itself unless Maven is creating a real, ongoing productivity drag rather than just being a less trendy choice. For most organizations with a stable, well-functioning Maven setup, I'd be cautious about recommending a wholesale migration purely on the basis of newer tooling being available.
I'd enforce semantic versioning discipline so consumers can understand the risk of a given upgrade from the version number alone, combined with clear release notes documenting breaking changes. I'd also maintain a reasonable deprecation window for any breaking change, giving consuming teams advance notice and time to migrate rather than releasing a breaking change without warning that forces an urgent, unplanned update on their side.
I'd audit the dependency tree for unnecessary or redundant dependencies that could be removed or consolidated, since dependency bloat often accumulates gradually without anyone deliberately choosing it, and check whether the project is unnecessarily pulling in a heavy dependency for functionality that a lighter alternative could provide. I'd also make sure the team is using a reasonably close, reliable repository mirror, since dependency resolution speed can be meaningfully affected by network latency to whatever remote repository is configured.
I'd review why the dependency's scope wasn't correctly set to test or provided, since that's typically the root configuration mistake, and add an automated check, like a build step that verifies expected artifact contents or fails if a known problematic dependency is detected in the final package. I'd treat this as a process gap worth fixing broadly rather than just correcting the one affected project, since the same mistake is likely possible across other projects using similar configuration patterns.
I'd keep the WAR module's packaging configuration isolated to that specific module's own POM rather than letting it influence the parent's shared configuration, since WAR-specific settings like a web.xml deployment descriptor or servlet dependencies with provided scope don't apply to the JAR-packaged modules. I'd also make sure the parent's dependencyManagement stays packaging-agnostic so both types of modules can inherit shared version management without conflicting assumptions about how they'll be packaged.
8-10 Years
I'd establish a centrally managed bill-of-materials with commonly needed, pre-approved dependency versions that teams can adopt with minimal friction, paired with a lightweight, fast-turnaround process for a team to request approval of a new dependency or version that isn't already covered. I'd measure the standard's success by how rarely teams feel compelled to bypass it, since a governance process that's too slow or rigid tends to get quietly worked around rather than genuinely followed.
I'd quantify the cost of the current state in concrete terms, like the engineering hours lost to slow or unreliable builds, the security risk from projects running years-outdated dependencies, and the onboarding friction new engineers face working across inconsistently configured projects. I'd propose a phased modernization plan that delivers value incrementally, starting with the highest-impact or highest-risk projects, rather than asking for a large upfront investment to modernize the entire portfolio simultaneously.
I'd require automated dependency vulnerability scanning integrated into every project's CI pipeline, with a defined, risk-based remediation timeline scaled to the severity of a given vulnerability, rather than relying on individual teams to manually track this themselves. I'd also prioritize scanning and remediation more aggressively for actively used, externally exposed projects than for genuinely low-risk internal tools, since a uniform response regardless of actual risk tends to spread remediation effort inefficiently.
I'd centralize configuration where inconsistency creates real cross-team cost, like core dependency versions, compiler settings, and security-relevant plugin configuration, while leaving teams flexibility in configuration specific to their own project's particular needs. I'd expect that boundary to shift as the organization scales, since what genuinely needs central control tends to expand as more shared infrastructure and cross-project dependencies accumulate.
I'd mandate strict semantic versioning enforced as part of the release process itself, ideally with automated tooling that flags a release as a breaking change based on actual API differences rather than relying purely on the releasing team remembering to bump the major version correctly. I'd also establish a standard communication channel, like a shared changelog feed or notification system, so dependent teams have a reliable way to learn about upcoming changes rather than discovering a breaking change only when their own build fails.
I'd require projects to avoid unpinned version ranges and minimize reliance on SNAPSHOT dependencies outside active development, and encourage adoption of dependency locking or a similar mechanism that captures the exact resolved dependency tree at release time. I'd frame this as a standard specifically because build reproducibility failures tend to surface at the worst possible time, like needing to rebuild an old release to patch a critical security issue, when the original build environment and its exact dependency versions are otherwise hard to reconstruct.
I'd default strongly toward established, well-maintained plugins from the broader ecosystem for standard build tasks, reserving custom plugin development for genuinely organization-specific needs the existing ecosystem doesn't address. I'd require any custom plugin to have a clear owning team and documented maintenance commitment, since an unowned internal plugin tends to become a fragile, poorly understood dependency that's hard to safely modify or retire later.
I'd set a differentiated cadence based on project criticality and exposure, requiring more frequent updates for externally facing or security-sensitive projects while allowing lower-risk internal tools a more relaxed schedule. I'd enforce the cadence through automated tracking and reporting rather than relying on teams to self-report compliance, since visibility tends to matter more than the policy itself in getting genuine adoption.
I'd apply least-privilege access control so teams can publish to their own designated repository paths without broad write access across the entire internal repository, reducing the risk of an accidental or malicious overwrite affecting unrelated teams' artifacts. I'd also require a clear approval or review process for any change to shared, widely depended-upon artifacts specifically, since those carry disproportionate blast radius compared to a team's own internal-only artifacts.
I'd standardize on a small set of consistently measured metrics, like median build duration and CI failure rate, collected automatically rather than self-reported, so leadership and platform teams can see genuine trends across the whole portfolio rather than relying on anecdotal complaints to surface which projects need attention. I'd make this data visible enough that teams have their own incentive to keep their build healthy, rather than it being purely a top-down monitoring exercise.
I'd require a staged rollout, validating the change against a representative sample of dependent projects before it's applied organization-wide, rather than pushing a change to the shared parent POM or plugin configuration directly to every project simultaneously. I'd also maintain a clear rollback path, since a shared configuration change that turns out to be problematic needs to be reversible quickly without every dependent team needing to individually intervene.
I'd assess each legacy project's business criticality and actual risk exposure, prioritizing modernization for projects where the outdated version poses a genuine security or support risk, rather than trying to modernize every legacy project uniformly regardless of actual impact. I'd frame modernization work in terms of the concrete risk it reduces, since that framing tends to compete more successfully for prioritization against feature work than a purely technical currency argument.
I'd require the releasing team to identify all known consumers and give them advance notice with a clear migration guide before the breaking release goes out, rather than allowing a major version release to surprise dependent teams with no warning. I'd also encourage maintaining the previous major version's branch for critical bug fixes during a reasonable transition window, so consuming teams aren't forced into an urgent, unplanned migration the moment the new version ships.
I'd set a minimum supported Maven version policy tied to security and compatibility considerations, enforced through automated checks like the Maven Enforcer Plugin's version requirement rule, rather than relying on teams to voluntarily stay current. I'd pair the minimum policy with a reasonable grace period for teams to upgrade when the minimum moves forward, so the standard doesn't create an abrupt, disruptive deadline for every project simultaneously.
10+ Years
I'd focus the platform team's early efforts on the handful of shared concerns causing the most cross-team pain if left ungoverned, like inconsistent dependency management or fragmented CI pipeline configuration, rather than trying to own every aspect of how each team structures their own project. I'd measure the team's success by how much faster and more reliably product teams can build and release, not by how much of the organization's build configuration the platform team directly controls.
I'd walk through a specific instance with them where a narrow fix caused unintended downstream friction for others, since seeing the concrete consequence tends to build the right instinct faster than being told about it abstractly. I'd coach them to habitually ask who else might be affected by a build configuration change before making it, and give them a chance to practice that by having them lead a small, organization-facing build improvement themselves with my guidance.
I'd translate the investment into business terms leadership already tracks, like engineering hours lost to slow or unreliable builds, security risk from outdated dependencies across the portfolio, and the onboarding friction new engineers face navigating inconsistent project setups. I'd back this with a concrete past example where a build or dependency issue caused a costly production incident or delayed a release, since a specific example tends to land better with executives than an abstract technical argument.
I'd plan around durable principles, like keeping build configuration consistent and centrally maintainable, and treat specific tooling choices as implementation details that should evolve as the ecosystem matures rather than fixed decisions made once. I'd revisit the vision periodically rather than treating it as permanent, since the right balance between centralized standardization and team flexibility tends to shift as the organization and its number of projects grows.
I'd bring both into a conversation grounded in the specific problems the shared parent POM solves and the specific friction it introduces for teams with genuinely different needs, since a disagreement like this often reflects real differences in project context rather than one engineer simply being wrong. I'd look for a middle ground, like making the shared parent strongly recommended with a clear, documented exception process, and if a genuine tradeoff remains, I'd make the final call myself with reasoning tied to the organization's broader priorities.
I'd give strong senior engineers real ownership of significant build infrastructure decisions for their area, with me available as a sounding board rather than making the call for them, since building that kind of judgment requires actually living with the consequences of a decision rather than only hearing about tradeoffs secondhand. I'd also create regular venues where these emerging leads present and defend their decisions to peers, since that kind of scrutiny sharpens both their technical thinking and their ability to communicate rationale to stakeholders.
I'd start with a pilot on a team that's willing and has a genuine pain point the consolidation solves, letting that team's concrete success build the case rather than trying to convince every team with a hypothetical pitch upfront. I'd also make the migration path itself as low-friction as possible through shared tooling and clear, well-tested migration guidance, since teams are far more willing to prioritize a change that doesn't cost them significant unplanned time away from their own priorities.
I'd lead an immediate, transparent incident response, making sure affected teams have clear communication and a realistic remediation timeline rather than silence while a fix is worked out, since that trust matters as much as the technical fix itself in the moment. I'd follow with a blameless postmortem focused on why the library's own release and testing process didn't catch the issue before it reached every dependent team simultaneously, since a shared dependency failing widely is usually a process gap as much as a code bug.
I'd centralize the things that create genuine cross-team risk or cost if inconsistent, like core dependency version governance, security-relevant configuration, and shared CI pipeline standards, while leaving teams flexibility in configuration specific to their own project's particular needs. I'd expect that boundary to shift over time, since what needs central control tends to expand as more of the organization's shared infrastructure and cross-project dependencies genuinely depend on consistency.
I'd make good build practice visible and valued the same way feature delivery already is, through practices like regular dependency health reviews and by recognizing teams and engineers who invest in keeping their build configuration clean and secure rather than only celebrating fast feature shipping. I'd also make sure leadership's own messaging and priorities don't inadvertently reward only shipping speed in performance conversations, since a team quickly learns what's actually rewarded regardless of what's stated in a values document.
I'd avoid mandating immediate standardization on day one given how disruptive that would be layered on top of the reorg itself, and instead give the newly combined team space to jointly evaluate and converge on shared conventions over a defined transition period with my guidance on the key tradeoffs at stake. I'd make sure the eventual standard genuinely draws from what worked well across the merged teams rather than simply defaulting to whichever team's approach happened to be largest going into the reorg.
I'd track outcomes tied to real business impact, like reduced build and release cycle time, fewer production incidents traceable to build or dependency issues, and faster onboarding time for new engineers working across the organization's projects. I'd present these as a narrative connecting build discipline to engineering velocity and reliability rather than purely technical metrics like the number of projects on a shared parent POM, since that's what actually resonates in an executive conversation.
I'd point out concretely how that habit is creating a bottleneck where the team routes hard problems straight to them instead of building the confidence to work through them independently, since that pattern often isn't visible to the lead themselves until it's named directly with specific examples. I'd encourage them to shadow rather than solve on the next few escalations, guiding a team member through the diagnosis instead of taking over, and check back with them afterward on what they noticed about the team's growth.
I'd default strongly toward well-supported ecosystem tools and conventions, reserving custom internal tooling for the specific gaps that genuinely matter at the organization's particular scale and aren't well served by anything available off the shelf. Custom tooling carries an ongoing maintenance cost that's easy to underestimate at the time it's built, so I'd want a clear, periodically revisited justification for anything the organization commits to owning long term.




