AI Agents and Stack-Agnostic Development: Sortly Case Study | JetRuby

How AI agents enable stack-agnostic development in production engineering. A real-world JetRuby case with Sortly shows how Ruby engineers successfully delivered production-grade Kotlin code using an AI-assisted workflow without losing speed or quality.

alt post cover

From “We Hire for Stack X” to Stack-Agnostic Teams

For years, engineering strategy followed a predictable rule: your stack defines your hiring. If you run Ruby, you hire Ruby engineers. If you introduce Kotlin, you look for Kotlin specialists.

That model used to work — until it started slowing companies down.

Today, engineering leaders face a different reality:

  • Hiring for a specific stack is slow and expensive
  • Strong engineers are scarce and expensive across ecosystems
  • Product roadmaps move faster than hiring pipelines
  • Architecture evolves continuously, not in isolated rewrites

As a result, “we need engineers in stack X” increasingly becomes a bottleneck rather than a safeguard.

At the same time, AI-driven development has matured into a backbone of the modern workflows allowing some teams to ship updates in weeks, whereas others require months to do the same.

This creates a shift that is easy to underestimate:

AI agents and stack agnostic development img 1 development

Stack-agnostic development is no longer theoretical — it is operational.

But only under one condition: it must be process-driven and supported by clear guardrails.

This article explains how that works in production — based on JetRuby’s experience working with Sortly.

The Sortly Context: Product, Business & Technical Reality

Sortly is a picture-driven asset management platform that helps users organize and track physical items in a simple, visual way — from everyday moving and household organization to more complex inventory use cases such as managing collections or business assets.

At the time of our engagement, Sortly already operated as a mature, multi-platform product with established mobile applications and an actively evolving web platform. JetRuby’s team was responsible for the web frontend, backend development, and QA, working alongside the client’s internal and external teams.

Over time, as the product and infrastructure continued to evolve, the client initiated a gradual backend migration toward Kotlin. JetRuby’s engineering team participated in this transition while continuing active development of the existing Ruby-based systems and the web platform itself.

Challenges

The key constraint was not feature development itself, but maintaining compatibility across platforms evolving at different speeds.

The web platform evolved rapidly and often became the primary surface for new functionality, while mobile applications operated under stricter release constraints. Unlike the web, mobile clients could not be updated instantly — changes depended on users installing new app versions, while many users continued running older releases for extended periods of time.

As a result, backend changes had to preserve backward compatibility with existing mobile clients, even as the web platform continued to evolve. Both platforms needed to develop in parallel without breaking production behavior for users on older mobile versions.

The complexity of the system made this even more challenging. Processing a single core request could involve interactions with around 30 database tables representing different parts of the platform accumulated over years of development.

A full rewrite would have required revalidation of a large number of interconnected flows. Even migrating a single endpoint often meant reimplementing cross-cutting concerns such as authentication, authorization, caching, and request validation, since these responsibilities were not fully isolated at the service boundary level. As a result, upfront estimation remained inherently unreliable.

This led to a core engineering challenge:

How do you evolve a live, production-grade system across multiple platforms without destabilizing existing clients or slowing delivery?

Solution

To address this, our backend team introduced a second version of the existing API dedicated to the web application. This allowed new functionality to be developed independently while preserving full compatibility with the iOS system.

It is important to clarify that this was not a full rewrite. Instead of rebuilding the system from scratch, the team approached the transition as a gradual, production-safe refactoring process.

Endpoints were migrated one by one, while the existing system remained fully operational. At any given moment, both versions of the same endpoint could coexist, allowing the team to validate new implementations without risking system stability.

From the client’s perspective, nothing changed — the product continued to function as usual while the underlying implementation evolved. At the same time, the migration process itself was highly non-trivial.

It was difficult to estimate upfront, required careful coordination, and progressed gradually. Even with the chosen approach, moving existing logic to Kotlin remained a time-consuming effort due to the complexity of the system and the need to preserve production stability.

On the frontend, we built a fully responsive web application aligned with Sortly’s visual-first product philosophy, while introducing web-specific usability improvements where appropriate.

Result

The result was a stable, production-ready web application that extended Sortly’s mobile experience into the web, maintaining consistency across platforms while preserving the simplicity and visual clarity that defines the product.

Technology Stack

ReactJS, Redux, Elasticsearch, Ruby on Rails, REST API.

Why This Matters

This type of system is not unusual in mature SaaS products. As systems evolve, engineering teams are constantly constrained not only by what is technically possible, but by what is safe in production environments.

In such contexts, architectural decisions are less about greenfield design and more about controlled extension, backward compatibility, and minimizing risk across interconnected systems.

In this class of systems, constraints are primarily driven by production coupling rather than implementation complexity.

Ruby Engineers, Kotlin Code

Importantly, this transition was not an obvious or universally supported decision. At the beginning, the engineering team was skeptical about introducing Kotlin into an already complex production system.

As one developer described it:

“We felt the complexity of the system was underestimated. From our perspective, there was nothing we couldn’t already solve within the existing stack.”

In hindsight, both perspectives turned out to be partially correct: the migration effort was indeed more complex than initially expected, but the long-term impact on code quality and engineering practices was also underestimated.

This is not a superficial transition. Ruby and Kotlin differ in ways that directly affect production systems:

Dimension Ruby Kotlin
Typing Dynamic Static
Error handling Runtime-heavy Compile-time guarantees
Idioms Flexible Explicit and structured
Tooling Lightweight Strong IDE/compiler reliance

For a Ruby engineer, this introduces real friction:

  • Understanding type systems and null safety
  • Writing idiomatic Kotlin instead of “translated Ruby”
  • Navigating unfamiliar frameworks and conventions

From a CTO’s perspective, the risks are clear:

  • Non-idiomatic, hard-to-maintain code
  • Subtle bugs due to misunderstanding language constraints
  • Reduced review quality due to lack of stack expertise

These are valid concerns — and exactly where most stack-agnostic attempts fail.

How We Made Stack-Agnostic Safe

AI agents and stack agnostic development img 2 development

The key principle is:

Stack-agnostic development is not about engineers knowing every stack. It’s about building a system where the stack difference becomes manageable.

In addition to AI-driven workflows, a critical part of making this transition safe was how the migration itself was structured.

Instead of moving entire endpoints at once, the team decomposed logic into smaller parts and migrated them incrementally within the same request flow.

Critically, AI was not the primary enabler of this transition. The foundation was a carefully designed migration strategy, strict architectural boundaries, and incremental delivery.

AI became a thing later and allowed team to automate trivial boilerplate job, keeping the engineering and architectural decisions in Human hands.

We implemented an AI-assisted workflow across five stages: design, coding, review, testing, and documentation.

1. Design: Remove Ambiguity Before Code

In an unfamiliar stack, unclear requirements multiply risk. We shifted more effort into structured design by introducing detailed technical specifications, explicit API contracts, and clearly defined edge cases.

AI was used as a static analysis layer over service specifications:

  1. detection of missing edge cases
  2. validation of contract completeness
  3. generation of implementation constraints aligned with Kotlin type system

A key safety mechanism in this process was the use of feature flags.

At runtime, feature flags determined whether specific parts of the logic were executed in Ruby or Kotlin. This allowed the team to gradually shift responsibility between implementations without introducing breaking changes.

This approach enabled incremental rollout, targeted testing of specific logic segments, and instant rollback to a stable version when needed.

In practice, migration often happened not only at the endpoint level, but also within individual request flows.

Instead of rewriting an entire endpoint at once, specific parts of its logic were gradually reimplemented in Kotlin and activated via feature flags. A single request could pass through both Ruby and Kotlin layers, depending on which parts had already been migrated.

This allowed the team to decompose even complex operations into smaller, manageable steps.

AI agents and stack agnostic development img 3 scaled development

Example (typical workflow):
An engineer drafts a service spec → AI is used to expand edge cases and validate flows → the spec is refined before any code is written.

Outcome: engineers work from clarity, not assumptions.

2. Coding: AI as a Translation Layer

During implementation, AI functioned as a translation layer between service specifications and Kotlin implementations. Typical usage patterns included generating Kotlin scaffolding from structured specifications, translating familiar patterns such as service objects and validations into Kotlin equivalents, and suggesting the correct use of language features like data classes, null safety, and immutability.

Important constraint: All generated code required manual review against the original specification. AI output was treated as non-authoritative.

In practice, AI-generated code was rarely production-ready on the first attempt. Engineers had to actively refine outputs to align with the system’s architecture and constraints, and in some cases reject suggestions that were technically correct but not suitable for the codebase.

3. Review: Separate Logic from Language Expertise

Code review was split into two independent validation layers:

Human review:

  • Business logic correctness
  • Architectural consistency
  • Alignment with system design

AI-assisted review:

  • Idiomatic Kotlin usage
  • Type safety issues
  • Potential edge cases and nullability risks

This allowed us to maintain quality even without deep Kotlin expertise in every reviewer.

4. Testing: Behavior Over Assumptions

Testing was used as a consistency enforcement layer across migrated and non-migrated implementations.

We emphasized strong unit test coverage, explicit validation of edge cases, and thorough integration testing for service interactions.

AI was used to generate test cases from specifications, expand edge case coverage, and identify missing scenarios.

Another important benefit of this approach was the ability to test changes in isolation. Instead of validating entire endpoints, engineers could focus on specific parts of the logic that were migrated, making testing more targeted and effective.

Example:
Given a service spec, AI suggests boundary conditions (e.g., null inputs, invalid states), which are then explicitly covered by tests.

This reduced reliance on intuition and increased confidence in behavior.

5. Documentation: Make Knowledge Explicit

We documented design decisions, Kotlin-specific patterns, as well as key trade-offs and constraints, while AI-assisted documentation helped keep materials aligned with the implementation, standardize explanations, and reduce maintenance overhead.

In addition, the team introduced explicit architectural guidelines covering module boundaries, dependency rules, and layer responsibilities.

Rather than limiting developers, these constraints reduced ambiguity and made it easier to write consistent, maintainable code. They also significantly improved onboarding speed, as new engineers could rely on documented patterns instead of implicit knowledge.

In practice, this included a layered architecture with clearly defined responsibilities and strict control over dependencies between modules. This architecture was heavily influenced by principles such as explicit dependency management, inversion of control, and separation of concerns.

The team moved away from tightly coupled service logic toward smaller, more isolated components, often communicating via well-defined interfaces or events. This reduced the need for large, monolithic services and made the system easier to evolve and test.

The system was structured so that different parts of the application could evolve independently, reducing coupling and making it possible for multiple engineers to work in parallel without conflicts.

Outcome: knowledge became shared, not implicit.

What Changed for Sortly

The migration model introduced observable changes in delivery characteristics.

Onboarding latency reduction for secondary stack contributions

Ruby engineers started contributing to Kotlin services within approximately 2–3 weeks, compared to a typical onboarding time of 2–3 months when adopting a new backend stack without similar process support.

Not because they fully “learned Kotlin,” but because:

  • The process reduced what needed to be learned upfront
  • AI handled much of the translation layer

Maintained Production Quality

Code quality remained stable, with no visible increase in defect rates, no accumulation of non-idiomatic technical debt, and consistent readability across services. This was largely possible because the team avoided “big bang” changes and instead delivered incremental updates under controlled conditions, often releasing improvements every 1–2 weeks behind feature flags.

This is a critical point: Stack-agnostic did not mean lower quality.

Stable Delivery Velocity

The team continued shipping features without a noticeable slowdown, avoiding the typical “transition dip” associated with adopting a new stack and effectively mitigating the key risk for engineering leadership — introducing new technology without losing delivery momentum. 

This was supported by a shift toward incremental delivery, where improvements were released in small, controlled batches every 1–2 weeks, often behind feature flags.

Reduced Hiring Dependency

Sortly did not need to immediately hire Kotlin specialists, as existing engineers remained productive, hiring pressure decreased, and resource allocation became more flexible.

Where Human Expertise Still Matters

AI did not change ownership boundaries. It changed execution pathways.

Human expertise remained critical in defining architecture and system boundaries, making trade-offs between performance, readability, and scalability, validating business logic, and identifying when AI suggestions were incorrect or suboptimal; in practice, some AI-generated solutions were technically correct but non-idiomatic, over-engineered for the problem, or missing important domain constraints, which engineers were responsible for identifying and correcting.

This is what makes the system safe: AI assists, but does not decide.

Why This Is More Than Just Sortly

 

The key takeaway is not that “Ruby engineers can write Kotlin.”

AI agents and stack agnostic development img 4 development

The real takeaway is:

With the right AI-driven process, stack dependency becomes a manageable variable — not a constraint.

This has broader implications:

  1. Hiring: You can prioritize strong engineers over narrow stack expertise.
  2. Architecture Evolution: You can introduce new technologies without full team restructuring.
  3. Legacy Systems: Older stacks become less limiting—you can extend and evolve them incrementally.
  4. Team Resilience: Your delivery capability becomes less dependent on specific technologies.

One of the key realizations from this experience was that many of the improvements were not tied to Kotlin itself. One of the most significant changes was not technical, but cognitive.

Engineers had to shift from flexible, implicit patterns toward more explicit, structured thinking — especially around data modeling, contracts, and dependencies. This shift, more than the language itself, had a lasting impact on code quality.

The migration forced the team to adopt stronger engineering practices — clearer boundaries, better data modeling, and more explicit contracts.

These practices are not language-specific, but are often only discovered when teams step outside of their familiar technology stack.

This is what turns stack-agnostic development into a strategic advantage.

Takeaways for Engineering Leaders

1. Treat Process as the Core System

Stack-agnostic capability is built — not assumed.

2. Use AI to Reduce Risk, Not Just Effort

Focus on:

  • Code translation in unfamiliar stacks
  • Review assistance
  • Test generation

3. Keep Ownership Human

AI supports execution. Engineers remain responsible for decisions and quality.

4. Strengthen Validation Layers

Code review and testing are what make cross-stack work safe.

5. Start Small and Prove It

Apply this approach to a single service or module before scaling.

Closing Thought

Stack-agnostic development is often misunderstood as a claim that engineers can work in any language without friction.

That’s not the point.

AI agents and stack agnostic development img 5 development

The point is:

With the right process, AI-enabled personnel, architectural oversight, and guardrails, teams can deliver production-quality code outside their primary stack—without sacrificing speed or reliability.

The Sortly case demonstrates that this is not theoretical. It is already happening in real production environments.

JetRuby’s role in this is not just writing code — it’s designing engineering systems that remain effective under uncertainty, whether that uncertainty comes from evolving stacks, changing architectures, or constrained hiring markets.

If you want to explore stack-agnostic development with AI agents in your product, let’s talk.

You may also find interesting

Contact us

By submitting request you agree to our Privacy Policy

By submitting request you agree to our Privacy Policy

Contact us

By submitting request you agree to our Privacy Policy