Context and Challenge
Guesty started as a Y Combinator-backed startup and grew rapidly over the last decade. As the team focused on building new features and growing the product, code scalability was not a pressing issue at first. Many core services were written years ago in an outdated version of Node.js and stored in a single repository with no TypeScript.
However, over time, this approach caused several problems:
- Code difficult to manage and understand: The large codebase lacked structure, which made it hard for engineers to make changes safely.
- Security risks: The outdated codebase posed potential vulnerabilities and lacked modern security practices.
- Scalability issues: Adding new features could unintentionally break existing functionality due to hidden dependencies.
- AI integration challenges: The large and unstructured codebase led to AI hallucinations, preventing the effective use of AI tools for coding or debugging.
- Reduced productivity: Developers needed to spend more time reading, debugging, and testing code because of its outdated style and lack of type safety.
Guesty decided that in order to support future growth, improve performance, and embrace AI-first development, the legacy system needed to be modernized.
Client’s Objectives
Guesty partnered with Forbytes with the following goals in mind:
- Improve code maintainability: Make the system easier for developers to read, understand, and improve.
- Support scalability: Ensure that new features can be added without slowing down performance or breaking existing logic.
- Modernize gradually: Avoid system disruption by updating components one step at a time.
- Eliminate security vulnerabilities: Move away from outdated packages and frameworks.
- Enable AI-first development: Create a cleaner codebase that could work effectively with AI assistants.
- Stabilize the platform: Reduce bugs and improve system stability through better architecture and test coverage.
How We Approached the Project
To ensure a smooth modernization journey, our team followed a structured process:
- Legacy system analysis: We analyzed the current system to understand what services it included, how it was used, and where the biggest pain points were.
- Problem identification and prioritization: Working closely with Guesty’s teams, we defined the biggest issues in the system and selected the most critical domains to modernize first.
- Process vision and roadmap: Together, we created a clear roadmap for modernization, aligning technical goals with business needs.
- Choosing the right approach: We selected a step-by-step decomposition strategy, moving code from the monolithic repository to distributed services one piece at a time.
- Implementation process: For each component, we broke down the logic into manageable parts, cleaned and updated the code, and moved the logic to a new service.
- Risk management and monitoring: We created quality metrics and risk plans to track progress and prevent disruptions.
Legacy Modernization Process
Our goal was simple: make the system clean, scalable, secure, and ready for the future. To do that, we needed to gradually move away from the old code and create a new, modular architecture made up of independent services. The old repository will eventually become unused and fully replaced by these new services.
Here’s how the modernization works, step by step:
1. Splitting the legacy code into smaller, manageable parts
Instead of dealing with one massive repository, we broke it down into logical domains — features or areas of responsibility that could be handled separately.
2. Analyzing what each piece of code does
Understanding old logic can be tricky, especially without documentation. So, we use AI tools to help us read and understand the code, figure out what it’s responsible for, and identify dependencies.
3. Cleaning and upgrading the code
We refactor the logic using modern coding practices. That means rewriting it in TypeScript, improving the file structure, removing redundant or duplicated code, and making sure everything is easier to understand.
4. Building a new service for the logic
Once cleaned and updated, the logic is moved to a new microservice that’s separate from the monolith. This gives us better control, better scalability, and fewer side effects when updating features.
5. Creating new endpoints
For each new service, we build fresh APIs using clean architecture principles. These endpoints will eventually replace the old ones.
6. Testing everything
We run thorough tests using Postman and other tools to make sure the new service works exactly as expected and doesn’t break anything.
7. Deploying and monitoring the updated service
Once testing is done, the updated service is released to production. The legacy logic is no longer used for that domain.
We repeat this process for each part of the old codebase. This way, we’re building a cleaner, faster, and more secure backend that will serve Guesty’s users for years to come.
Results and Impact
Thanks to this modernization effort, Guesty is now in a much stronger position to support future growth:
- Modern architecture with distributed services
- Improved developer productivity with TypeScript and clean code structure
- AI-ready environment for future AI-powered development
- Increased system stability with better test coverage and less fragile code
- Reduced security risks from outdated dependencies
- Faster delivery of new features thanks to modular architecture
Technology Stack
Backend: Node.js, Typescript, Nest
Frontend: React, Redux
Database: MongoDB
Test tools: Mocha, Jest, Chai, Sinon
Cloud storage: Snowflake
Cloud platform: AWS, Google Cloud
Logs/Monitoring tools: Grafana, Prometheus, AWS CloudWatch, InfluxDB, Coralogix, Kibana, Datadog
Virtualization: Docker, Nomad
Continuous integration: Circleci, Jenkins, Split.io, GitHub, Vault, Jira
Other tools: Redis, Rabbit, MQKafka
Key Takeaways and Lessons Learned
- Gradual modernization is an effective strategy: Breaking legacy code into small parts made the process manageable and safe.
- Collaboration is key: Working closely with Guesty’s R&D and DevOps teams ensured alignment and fast feedback.
- AI can speed up analysis: AI tools helped us quickly understand code responsibilities and dependencies.
- TypeScript matters: Adding type safety reduced bugs and improved code quality.
