BACK
06 / REAL-WORLD PRODUCTS

Full-Stack Developer @ FishyVisions.

In January 2024, I joined FishyVisions, a small US startup, as its third developer. It was my first professional full-stack role, and with a team that small, there wasn’t much room to specialize.

We released new features almost every week. I worked across the stack, collaborated directly with designers and gradually took responsibility for entire parts of the product.

The product was Quely, a collaboration tool designed to help teams turn vague Jira tickets into actionable requirements. Instead of treating a ticket as a static description, Quely made it a place for discussion, documentation and clarification, with AI helping teams identify missing information before development began.

Building it taught me a lot about software engineering, particularly the decisions that happen before and after writing the code.

The product was commercially available; its code, customer data and internal architecture were not.

Making Jira feel like part of the product

Quely wasn’t meant to replace Jira. It needed to work alongside it, letting teams collaborate in Quely while keeping their existing Jira projects in sync.

Making that experience seamless was one of my main responsibilities.

I implemented Jira OAuth authentication through NextAuth, with per-user access and refresh tokens stored in MongoDB. A central server-side Jira client handled credentials and token renewal, so the rest of the application didn’t have to.

Rather than fetching every issue directly from Jira, we mirrored them into our database. This made boards more responsive, but also meant keeping two systems synchronized. Changes made in Quely were written back to Jira, while webhooks handled updates coming from the other direction.

  1. 01

    Authenticate

    Sign in through Jira or Google using NextAuth

  2. 02

    Resolve credentials

    Retrieve the user’s OAuth tokens and refresh them when needed

  3. 03

    Fetch and mirror

    Import Jira issues into Quely’s MongoDB database

  4. 04

    Write back

    Synchronize changes made in Quely to Jira

  5. 05

    Receive updates

    Process Jira-side changes through webhooks

The integration flow, from sign-in to inbound webhooks.

The architecture involved trade-offs. Mirroring improved responsiveness but introduced the possibility of stale data and synchronization conflicts. Using individual credentials allowed API operations to respect each user’s Jira permissions, but required careful token management.

It was my first real experience of how much complexity can hide behind a seemingly simple feature like connecting two applications.

Improving the codebase without stopping delivery

When I joined, Quely was built with JavaScript, Next.js, GraphQL and MongoDB through Mongoose.

One recurring source of problems was the boundary between frontend and backend. We would assume a certain data shape on one side, only to discover at runtime that the other side returned something different.

I proposed migrating the application to TypeScript and led the transition incrementally, allowing existing JavaScript code to continue working while we converted the codebase. I also introduced consistent formatting and commit hooks to make collaboration easier.

It was a meaningful improvement, although not a complete solution. We didn’t introduce type safety across the GraphQL boundary, so some of the original problems remained.

I also advocated replacing GraphQL with tRPC. After discussing the trade-offs with a colleague, however, I realized the migration would create more disruption than the benefits justified.

A cleaner technical solution isn’t automatically a better decision, especially when an existing system works and customers depend on it.

Knowing when not to call the model

Quely also used AI to generate documentation from discussions and help identify missing information in requirements.

One particularly interesting challenge was deciding when the AI should intervene.

An assistant that responds to every message quickly becomes distracting. Instead, the system used embeddings to retrieve relevant context from previous discussions and selectively triggered the AI when there appeared to be something useful to clarify.

The approach reduced unnecessary model calls and interruptions, although it introduced another trade-off: a selective trigger could also miss opportunities to help.

Working on AI features gave me a better appreciation for how important the surrounding product decisions are. Integrating a model is only part of the challenge. Making its behaviour useful, predictable and unobtrusive is just as important.

Learning to think beyond the implementation

Beyond these projects, I built real-time chat and timers, worked on authentication and role-based permissions, contributed to presence and planning poker, and developed work items and comments across the stack. I introduced optimistic updates and caching so the interface responded before the server did, and wrote the team’s clean-code guidelines.

With only three developers, there was plenty of opportunity to take responsibility. I worked closely with our designer, participated in design discussions and learned to narrow down ideas before committing to implementation.

Quely eventually launched commercially and reached real customers. It never operated at a particularly large scale, but the experience was valuable for a different reason: I got to see how technical decisions played out in a real product, with real constraints and people depending on what we shipped.

Looking back, the biggest change wasn’t the number of technologies I learned.

When I started, I was mostly concerned with figuring out how to build something. Over time, I became more interested in whether we should build it that way in the first place.

I became more comfortable questioning assumptions, weighing trade-offs, listening to better arguments and recognizing when changing something wasn’t worth the cost.

Those two years gave me my foundation as a full-stack developer, but more importantly, they changed how I approach engineering.

NEXT DECISION

When a working feature isn't working.