A Java application can speak PostgreSQL and still fail a Snowflake Postgres cutover for boring reasons.
This post is a demonstrative walkthrough. We use Keycloak, a 1.2MLOC production-scale open-source codebase, to show how Codemod Grep and Codemod Insights help eng and FDE teams plan, derisk, and track Snowflake Postgres–style migrations. Snowflake is the migration target in the scenario, not a Codemod customer or partner in this write-up.
Keycloak is an open-source identity and access management project. IT and security teams use it to authenticate and authorize users across an organization's applications, over OpenID Connect, OAuth 2.0, and SAML. Organizations that run it include IBM, UnitedHealth Group, Accenture, Shell, Oracle, Dell, Boeing, and Lenovo.
Keycloak earns the role: popular, large, and its Aurora deployment guide already wires an AWS JDBC wrapper URL, leans on writer-oriented routing, and pulls credentials through Kubernetes secrets. None of that is "non-Postgres." All of it is host-provider behavior that survives a hostname paste.
The question eng leads inherit is not "does JDBC work?" It is "which assumptions are still in the repo, who owns them, and how do we know the remainder is shrinking?"
Scope here is discovery, ownership, derisking, and progress, not running the migration. For transforms, campaigns, and validation end to end, talk to Codemod.
A hostname swap leaves the AWS wrapper, writer routing, and secret shape untouched
Snowflake Postgres accepts PostgreSQL clients. SSL is required. Certificate verification matters. Those facts are documented.
What stalls teams migrating toward that target is the second layer of configuration that grew up around a previous host:
- Deployment YAML still using
jdbc:aws-wrapper:postgresql://… - An explicit
software.amazon.jdbc.Driveroverride next to that URL - Operator CR fields that pull credentials from
usernameSecret/passwordSecret - Application defaults that append
targetServerType=primarywhen the parameter is absent - Native SQL call sites that belong on a review backlog, not an automatic rewrite list
Change only the hostname and you can leave the AWS wrapper driver, reader/writer routing habits, and secret shape untouched. The app still "connects to Postgres." It just connects with the wrong provider contract.
For FDE and platform teams, that is organizational work as much as technical work. App owners, image maintainers, and SRE disagree about scope until someone can point at files.
Grep the wrapper, the driver pin, and the routing Java adds when YAML omits it
Start with questions the repository can answer, then read the surrounding code so you do not over-count.
We ran YAML and Java ast-grep rules through Codemod Grep against keycloak/keycloak (indexed commit ebde947d…). The Java counts open those rules on public Grep. The first three counts are YAML, which that public index does not search, so they open the same files on GitHub.
ast-grep matches the parse tree, not raw text lines. You name a node kind, then narrow it. A plain_scalar rule finds the wrapper URL and the driver class. A block_mapping_pair rule matches the usernameSecret key and keeps the nested secret ref attached, so a test fixture whose value is the word usernameSecret does not become a second hit.
The YAML rules behind the first three rows:
# HA examples: Aurora wrapper JDBC URLs
id: yaml-aws-wrapper-jdbc-url
language: yaml
rule:
kind: plain_scalar
regex: "jdbc:aws-wrapper:postgresql:"
---
# HA examples: explicit AWS JDBC driver pin
id: yaml-aws-jdbc-driver
language: yaml
rule:
kind: plain_scalar
regex: 'software\.amazon\.jdbc\.Driver'
---
# Operator CRs and HA examples: DB username from a Kubernetes secret
id: yaml-username-secret
language: yaml
rule:
kind: block_mapping_pair
has:
field: key
has:
kind: plain_scalar
regex: "^usernameSecret$"The same shape of query powers the discovery package later. JSSG miners walk these node kinds and emit Insights metrics, including file-level URL and driver pairing.
keycloak.yaml with url: jdbc:aws-wrapper:postgresql://…software.amazon.jdbc.Driver still pinned?db-driver → Amazon driverusernameSecret?targetServerType?DefaultJpaConnectionProviderFactory.addPostgreSQLKeywords appends targetServerType=primary when absentcreateNativeQuery?JpaRealmProvider, cluster and revoked-token providerskeycloak.yaml with url: jdbc:aws-wrapper:postgresql://…software.amazon.jdbc.Driver still pinned?db-driver → Amazon driverusernameSecret?targetServerType?DefaultJpaConnectionProviderFactory.addPostgreSQLKeywords appends targetServerType=primary when absentcreateNativeQuery?JpaRealmProvider, cluster and revoked-token providersThose searches are narrow on purpose. Counting every db-url string as an independent connection would exaggerate the work. Keycloak's property mappers fold options into Quarkus datasource settings. Grep finds candidates; an engineer still decides what the hit means.



The targetServerType hit is the one worth repeating in reviews: absence searches on deployment YAML are not enough. Keycloak injects writer routing when the parameter is missing. If you only search manifests, you miss behavior the application supplies for you.

Validated Grep becomes a pairing board the status meeting can burn down
Grep is how you interrogate a repo in the moment. Insights is how the same questions become a shared planning surface.
We published a read-only discovery package, snowflake-postgres-discovery-bundle 0.1.0, that turns those miners into Insights metrics (AWS JDBC pairing, DB address roles, TLS, targetServerType, pool settings, native SQL, deployment signals, PG test containers, and more). Validated queries stop living in someone's terminal history and start living on a dashboard eng leads can open in a status meeting.
Insights screenshots below are from our Codemod org planning board for this demo: Snowflake → Postgres migration planning (Keycloak) on keycloak-java, backed by that package.

Discovery counts are soft by design. An earlier local pass of a pinned Keycloak release saw AWS-wrapper URLs in five files and the AWS driver class in four, including docs, generated HA examples, and a test profile. Grep on the indexed Keycloak commit above shows two wrapper-URL files and two driver files in the HA examples alone. File counts are not deployed-service counts. Inventory residual risk first, then decide which paths are in-scope deployment config vs upstream examples before you report a burn-down.
A useful baseline board answers questions leaders actually ask:
- How many in-scope deployment files still carry wrapper URLs or the paired AWS driver?
- How many TLS settings fail the agreed Snowflake Postgres rule for the cutover?
- Which CODEOWNERS still own residual matches?
- Did a deprecated pattern reappear after an earlier wave?
Pairing matters more than two independent string counts. "Which definitions still set a target URL but keep an incompatible driver?" needs fields related inside one document. Insights widgets that encode that pairing are what keep status from turning into spreadsheet fiction.

The planning board shows one wrapper pair left and a native-SQL queue that still needs owners
Widget previews for keycloak-java before we saved the org planning board (tests, docs, and generated examples included; planning signal, not production proof):
- Wrapper URL with AWS driverXML pairing; Java showed aws-driver-only in the sample
- 1
- AWS JDBC settings2 Java + 2 XML signals
- 4
- Database option definitions
- 2
- JDBC TLS settings
- 49
targetServerTyperouting- 6
- Native SQL call sites
- 54
- PostgreSQL test containers
- 3
- Additional datasources
- 24
- Pool maximums
- 26
- Deployment signals
- 54
- Wrapper URL with AWS driverXML pairing; Java showed aws-driver-only in the sample
- 1
- AWS JDBC settings2 Java + 2 XML signals
- 4
- Database option definitions
- 2
- JDBC TLS settings
- 49
targetServerTyperouting- 6
- Native SQL call sites
- 54
- PostgreSQL test containers
- 3
- Additional datasources
- 24
- Pool maximums
- 26
- Deployment signals
- 54
Deployment YAML in this sample was largely postgres-url-only once you filter for wrapper+driver pairing. The burn-down surface and the Grep HA-example hits are related but not identical units. That is the point of pairing widgets: string counts alone will mis-size the work.

That last distinction is where agent-assisted migrations go wrong. Models are happy to "fix" every createNativeQuery. Teams need a finite list, an owner, and a staging plan against Snowflake Postgres (login and session behavior, schema migration, concurrency, connection recovery), not hundreds of speculative rewrites.
Grep under model/jpa/ already gives a smaller slice of that backlog (9 matches in 5 files). Insights' 54 is the broader native-SQL miner across the package scope. Use both: Grep for the walkthrough, Insights for the shared remainder.




Discover, measure, and derisk on a shared board before you call the cutover done
- 01Index the repos in scopeWrite Grep questions for host-provider assumptions, not only "Postgres vs Snowflake SQL."
- 02Read hits in contextSeparate option definitions from prose, constants, and env references before sizing endpoint work.
- 03Promote stable queries into Insights metricsOr install a package like the discovery bundle, so coverage and residual risk are shared on a board like the Keycloak planning dashboard.
- 04Define deployment scope before reporting numbersUpstream docs and local fixtures belong on a different row than the deployment configs you intend to cut over.
- 05Track pairing and ownershipNot just raw match counts. CODEOWNERS and directory splits decide who gets the PR when you do move to execution.
- 06Treat native SQL and TLS as review queuesGive them explicit acceptance criteria. Staging against Snowflake Postgres is separate from a green Insights board.
- 07Keep any later transforms allowlistedWhen you move to execution, change reviewed deployment shapes (wrapper URL →
jdbc:postgresql://<snowflake-postgres-host>/…, paired driver rollback, TLS trust, secrets) via a tested workflow, not a global replace across upstream Keycloak.
- 01Index the repos in scopeWrite Grep questions for host-provider assumptions, not only "Postgres vs Snowflake SQL."
- 02Read hits in contextSeparate option definitions from prose, constants, and env references before sizing endpoint work.
- 03Promote stable queries into Insights metricsOr install a package like the discovery bundle, so coverage and residual risk are shared on a board like the Keycloak planning dashboard.
- 04Define deployment scope before reporting numbersUpstream docs and local fixtures belong on a different row than the deployment configs you intend to cut over.
- 05Track pairing and ownershipNot just raw match counts. CODEOWNERS and directory splits decide who gets the PR when you do move to execution.
- 06Treat native SQL and TLS as review queuesGive them explicit acceptance criteria. Staging against Snowflake Postgres is separate from a green Insights board.
- 07Keep any later transforms allowlistedWhen you move to execution, change reviewed deployment shapes (wrapper URL →
jdbc:postgresql://<snowflake-postgres-host>/…, paired driver rollback, TLS trust, secrets) via a tested workflow, not a global replace across upstream Keycloak.
A clean repository dashboard is one acceptance criterion. It does not prove pods already use the new database. Campaigns and deployment telemetry close that loop; this demonstrative post stops at planning and tracking.
This pass inventories JDBC, SQL, and tests while schema conversion keeps its own assessment
Grep and Insights do not replace workload assessment for warehouse waves, schema conversion, or AIM-style object dependency graphs. They answer application-repo questions around those waves: which JDBC URLs, datasources, SQL literals, and tests still point at the source system.
This approach also fails if you treat discovery counts as proof of production readiness, or if you let agents rewrite every native SQL hit without a review queue. The Keycloak walkthrough shows the discovery and measurement method for Snowflake Postgres–style migrations. It does not claim we migrated Keycloak's transactional database end to end, or that Snowflake endorsed or commissioned the work.
Agents should open the same Grep links and burn-down the status meeting already uses
Long-running migrations fail when every agent session restarts from "search the repo for JDBC" with a slightly different regex. Grep share links and Insights metrics are the durable layer: the same questions, the same pairing rules, the same burn-down.
If your team is planning a Snowflake Postgres migration (or a broader move that still needs application-repo evidence):
- Create a Codemod account and connect the repos in scope.
- Add the Codemod MCP so coding agents can run Grep, read Insights, and work against the same indexed corpus your eng/FDE team uses.
- Install or author discovery packages for the assumptions that matter in your stack, then put the metrics on a planning board before anyone generates PRs.
- For a guided PoV, pilot, or demo on a production-scale app, reach out to Codemod.
Speaking PostgreSQL is table stakes. Planning a Snowflake Postgres migration at enterprise scale means making host-provider assumptions visible, owned, and measurable, then letting agents operate inside that shared plan instead of inventing a new audit every week.
Understand & evolve your codebases with confidence
Give your agents the intelligence they need to reliably automate code maintenance and migrations at enterprise scale.