---
title: "Refactoring"
description: "36 posts about Refactoring, summarised, each linking to the original."
---

# Refactoring
> 36 posts about Refactoring, summarised, each linking to the original.

## Articles

### [Changing a polymorphic\_type in Rails](https://yomu.fyi/post/changing-a-polymorphic-type-in-rails.md)
- Company: [Shopify](https://yomu.fyi/company/shopify.md)
- Author: 2023-10-18
- Published: Feb 18, 2022
- Type: Explainer

Rails normally persists a polymorphic association’s type as the parent class name, so moving Car under Garage changes stored values from “Car” to “Garage::Car” and can break lookups for older Key records. The post presents an alternative for the Vehicle/Key example: override polymorphic\_name so Garage::Car and Garage::Boat resolve to stable arbitrary strings such as “car” and “boat,” reducing coupling between namespaces and database data. During transition, the association scope must explicitly accept both legacy class-name values and new arbitrary values, because Rails uses only the single value returned by polymorphic\_name when querying; existing rows can then be cleaned up with a migration, script, or MaintenanceTasks. After cleanup, the temporary unscope can be removed, while class and module renames require updating the mappings rather than database records. The approach adds complexity and is presented as worthwhile only when near-term naming changes are likely.


### [The Vitality of Core Web Vitals](https://yomu.fyi/post/the-vitality-of-core-web-vitals.md)
- Company: [Shopify](https://yomu.fyi/company/shopify.md)
- Author: 2023-10-18
- Published: Nov 4, 2021
- Type: Tutorial

Core Web Vitals were introduced by Google in 2020 as unified guidance for web UX, and their loading, interactivity, and visual stability metrics became one factor in ranking systems after rollout from mid-June through the end of August 2021. The article explains LCP, FID, and CLS, including how LCP identifies the largest visible element, FID measures delay before the browser responds to a first interaction, and CLS sums unexpected layout shifts. It gives good and poor thresholds of 2.5 seconds and 4 seconds for LCP, 100 milliseconds and 300 milliseconds for FID, and 0.1 and 0.25 for CLS, with intermediate scores needing improvement. Recommended improvements include updating dependencies and tree-shaking unused library code, preloading critical assets, improving server response time, lazy-loading below-the-fold content, compressing images, and reserving image space to prevent shifts.


### [Replenysh uses Supabase to implement OTP in less than 24-hours](https://yomu.fyi/post/replenysh-uses-supabase-to-implement-otp-in-less-than-24-hours.md)
- Company: [Supabase](https://yomu.fyi/company/supabase.md)
- Published: Oct 19, 2021
- Type: Problem & solution

Replenysh builds infrastructure connecting brands with local communities to track, monetize, and recover used packaging and materials. The engineering team previously maintained their own backend infrastructure, pairing custom Twilio and password authentication with a Haskell server and a Postgres database on Heroku. Seeking to accelerate development velocity and minimize ongoing DevOps maintenance during a codebase refactor, Replenysh required an alternative backend platform that preserved relational Postgres and Row Level Security. The team selected Supabase over alternatives like Firebase because it provided native Row Level Security alongside built-in authentication tooling. By adopting Supabase Auth, Replenysh successfully implemented an SMS one-time password system in less than 24 hours before deploying their mobile application to production.


### [A Kotlin Style .copy Function for Swift Structs](https://yomu.fyi/post/a-kotlin-style-copy-function-for-swift-structs.md)
- Company: [Shopify](https://yomu.fyi/company/shopify.md)
- Author: 2023-10-18
- Published: Sep 2, 2021
- Type: Tutorial

Immutable state objects are used to keep rendering code from modifying shared values, supporting clearer value origins, fewer bugs, and more focused testing. Kotlin data classes provide a copy function that replaces selected fields while preserving the rest, allowing the new object to become the source of truth. Swift structs lack an equivalent convenience, and defining a complete initializer plus a parameter-based copy method becomes cumbersome, particularly with many or nested properties. Optional properties expose a further issue: nil must sometimes mean “set this field to nil,” not “use the existing value.” A functional builder closure solves that distinction by collecting overrides while retaining the original struct’s values for other fields, and a Sourcery stencil can generate the initializer and builder boilerplate.


### [Diggin’ and Fetchin’ with TruffleRuby](https://yomu.fyi/post/diggin-and-fetchin-with-truffleruby.md)
- Company: [Shopify](https://yomu.fyi/company/shopify.md)
- Author: 2023-10-18
- Published: Aug 20, 2021
- Type: Explainer

The investigation starts with production Ruby code that chains Hash#fetch calls and uses an empty-hash default to return an IdentityObject for missing nested data. It compares that workaround with Hash#dig, which is cleaner but does not provide fetch's same control over missing keys, defaults, and explicit nil values. To prototype a combined Hash#dig\_fetch method, the author modifies TruffleRuby, using Primitive's hash\_get\_or\_undefined and undefined? to distinguish absent keys from keys containing nil, then replaces recursive traversal with an iterative implementation in shared Diggable logic. For a hash with nine nested keys, iterative changes lift dig from about 2.5M to 16M iterations per second and lift dig\_fetch from about 2.5M to 15.5M. The dig\_fetch prototype enables the original refactor, but it is not ready for general Ruby adoption because Array and Struct interoperability and corresponding MRI work remain.


### [How We Cut GrabFood.com’s Page JavaScript Asset Sizes by 3x](https://yomu.fyi/post/how-we-cut-grabfood-com-s-page-javascript-asset-sizes-by-3x.md)
- Company: [Grab](https://yomu.fyi/company/grab.md)
- Author: Gibson Cheng
- Published: Jul 29, 2021
- Type: Problem & solution

GrabFood.com faced high cloud costs while serving over 1 TB of weekly network egress across 175 million requests. To minimize egress and improve page performance, the engineering team audited and reduced their webpack dependencies using tools such as webpack-bundle-analyzer and dependency-cruiser. Their strategies included lazy loading rarely used libraries, unifying duplicate modules under single entry points, and substituting utility libraries like axios with native browser Web APIs. Additionally, altering implementation approaches, such as replacing client-side signed JWT cookie encoding with plain JSON strings, eliminated heavy Node dependencies like crypto. These optimizations reduced JavaScript static assets from 750 KB to 250 KB, decreased CloudFront costs by 20%, and accelerated build times by 3.6x.


### [App Modularisation at Scale](https://yomu.fyi/post/app-modularisation-at-scale.md)
- Company: [Grab](https://yomu.fyi/company/grab.md)
- Author: Amar Jain
- Published: Jul 13, 2021
- Type: Problem & solution

Grab transitioned its monolithic mobile application into a modular architecture to resolve increasing code conflicts, slow releases, and difficult team collaboration. The team decomposed the single module by establishing base infrastructure modules, shared UI and utility libraries, discrete feature modules, and bridge kit modules for inter-module communication. Dependency injection using Dagger ties these components together in the main app module while preventing feature modules from directly depending on one another. The architecture spans over 1,000 modules across the app, with more than 200 modules in the Grab Financial Group payments domain where over 95% of modules build in under 15 seconds. This approach accelerated Gradle CI and local builds through parallel compilation and caching, though it increased Gradle sync times, IDE memory usage, and configuration maintenance overhead.


### [Other Driven Developments](https://yomu.fyi/post/other-driven-developments.md)
- Company: [Shopify](https://yomu.fyi/company/shopify.md)
- Author: 2023-10-18
- Published: May 21, 2021
- Type: Explainer

The article argues that development models turn implicit lessons into shared, conscious practices as teams, codebases, and responsibilities grow. It presents Grep Driven Development, emphasizing expressive and consistent names so behavior remains searchable, refactorable, abstractable, and predictable, while warning that premature abstraction and locally “improved” naming can hinder later change. It also makes the case for Copy/Paste Driven Development as a deliberate, judgment-based tool, noting that duplication can be cheaper than the wrong abstraction and that abstractions with flags and conditionals may create their own costs. The article names Ego Driven Development and Stickler Driven Development among its four approaches, then concludes with Human Driven Development: choosing, bending, or breaking rules to make work easier for present and future teammates.


### [One Small Step Closer to Containerising Service Binaries](https://yomu.fyi/post/one-small-step-closer-to-containerising-service-binaries.md)
- Company: [Grab](https://yomu.fyi/company/grab.md)
- Author: Stan Halka
- Published: Feb 23, 2021
- Type: Problem & solution

Engineering teams at Grab initiated a transition to containerized microservices to standardize environments, enhance security, and decouple services from internal runtime tooling. During this migration, developers noticed that statically-linked Go service binaries were reaching bloated sizes over 100 MB. By analyzing the binaries using the open-source tool go-binsize-viz alongside the Go nm toolchain, the team visualized compiled symbols as interactive treemaps. This analysis revealed that 11 MB of unused message format symbols were being pulled in because a generic interface shared a directory with auto-generated streaming code. Restructuring the packages to isolate interfaces from generated code successfully decreased the binary size down to 78 MB.


### [Making the Supabase Dashboard Supa-fast](https://yomu.fyi/post/making-the-supabase-dashboard-supa-fast.md)
- Company: [Supabase](https://yomu.fyi/company/supabase.md)
- Author: Inian Parameshwaran
- Published: Dec 13, 2020
- Type: Problem & solution

Adding features to the Supabase single-page application dashboard risked performance regressions due to increasing JavaScript bundle sizes. To establish a baseline, the engineering team introduced next-bundle-analyzer and tracked Real User Monitoring alongside Core Web Vitals using Sentry. Optimization efforts focused on pruning dependencies, which included swapping Moment.js for day-js, replacing Joi with ajv, reverting crypto-js to version 3.3.0, and moving the 388 KB zxcvbn password module to a backend API. The team also implemented Next.js dynamic imports for heavy components like spreadsheet parsing, removed legacy server-side props to unlock Automatic Static Optimization, and configured long cache headers on assets. These architectural adjustments brought Core Web Vitals within recommended thresholds and lowered client-side page transition payloads below 200 KB of JavaScript.


### [Migrating Large TypeScript Codebases To Project References](https://yomu.fyi/post/migrating-large-typescript-codebases-to-project-references.md)
- Company: [Shopify](https://yomu.fyi/company/shopify.md)
- Author: 2023-10-18
- Published: Jul 29, 2020
- Type: Tutorial

Shopify’s merchant admin UI grew from a Ruby on Rails ERB-based front-end into a TypeScript, React, and GraphQL codebase, and its size eventually made VSCode’s TypeScript language tooling take roughly two to three minutes to initialize. The team investigated the bottleneck with TypeScript’s performance diagnostics, VSCode TSServer logs, and support from the TypeScript team, which recommended splitting the monolithic configuration with project references. They built the TypeTrack VSCode plugin to measure initialization time, then migrated dependency-graph leaf nodes such as packages and tests, creating project-level tsconfig.json files and references incrementally. The migration reduced initialization times for sampled files from about 155 seconds to 8–13 seconds. The post explains how to resolve missing module errors, respect project-reference restrictions, and continue upward through the dependency graph while distributing migration work across teams.


### [Refactoring Legacy Code with the Strangler Fig Pattern](https://yomu.fyi/post/refactoring-legacy-code-with-the-strangler-fig-pattern.md)
- Company: [Shopify](https://yomu.fyi/company/shopify.md)
- Author: 2023-10-18
- Published: Mar 11, 2020
- Type: Problem & solution

Shopify’s Ruby on Rails Shop model had grown beyond 3,000 lines, accumulated numerous responsibilities, and developed weak semantic boundaries, making store settings a target for extraction. The team used Flog’s ABC-based score—assignments, branches, and calls—to identify a particularly disordered area, then applied Martin Fowler’s Strangler Fig Pattern to move the locked\_settings behavior into Capital::SettingsToLock and a dedicated record/data source. The seven-step process defined and tested a new interface, redirected callers, introduced and backfilled the new data source, switched reads, and finally stopped legacy writes and removed the old column and code. Keeping the old system available during the transition supported incremental changes, monitoring, reversibility, and the stated no-downtime requirement; the completed example left the new system in place with legacy remnants removed.


### [Journey to a Faster Everyday Superapp Where Every Millisecond Counts](https://yomu.fyi/post/journey-to-a-faster-everyday-superapp-where-every-millisecond-counts.md)
- Company: [Grab](https://yomu.fyi/company/grab.md)
- Author: Renu Yadav
- Published: Dec 26, 2019
- Type: Problem & solution

Grab undertook an initiative to reduce startup time and improve time to interactive (TTI) on its passenger mobile app. Because local benchmarks failed to simulate real device and network conditions, the team instrumented code in production across 8–9 million daily users to capture p50 and p95 metrics. Initial gains came from caching service tiles between sessions and removing a startup animation, saving four seconds. Architectural changes followed, including converting iOS dynamic frameworks to static linking and merging others, while Android initialisation was refactored with Kotlin coroutines. Replacing a heavy third-party analytics library with an internal experimentation platform yielded further startup reductions.


### [How We Implemented Domain-Driven Development in Golang](https://yomu.fyi/post/how-we-implemented-domain-driven-development-in-golang.md)
- Company: [Grab](https://yomu.fyi/company/grab.md)
- Author: Kapil Chaurasia
- Published: Nov 21, 2019
- Type: Problem & solution

Building GrabPlatform's partner integration self-service portal initially resulted in an unstructured codebase where individual files exceeded 500 lines and lacked proper segregation. Modifying existing functions carried high risks of breaking functionality across imported source collections. To resolve this, the team restructured the Go application using Domain-Driven Design principles in coordination with product domain experts. They mapped business rules into bounded contexts, identified entities and aggregate roots, introduced repository interfaces, and utilized domain events for cross-context communication. The refactoring distributed core functionality evenly, simplified onboarding, and aligned technical terminology with business concepts.


### [Deconstructing the Monolith - Shopify](https://yomu.fyi/post/deconstructing-the-monolith-shopify.md)
- Company: [Shopify](https://yomu.fyi/company/shopify.md)
- Author: 2023-10-18
- Published: Feb 21, 2019
- Type: Problem & solution

Shopify describes why its decade-old Ruby on Rails monolith became difficult to change as the codebase and development organization grew. High coupling and absent boundaries made innocuous changes trigger unrelated test failures, slowed CI, and forced developers to absorb excessive domain context, while microservices would add deployment, infrastructure, network, and coordination costs. The chosen path was a modular monolith: retain one codebase and deployment unit while separating domains through strictly enforced component boundaries. Shopify’s Componentization effort was informed by a developer survey and uses Wedge to score components and report violations, including cross-component associations and calls that bypass explicitly public interfaces. Boundary enforcement at runtime remains a future goal, but dependency isolation has already enabled replacing a legacy tax engine with a new tax calculation system.


### [Introducing the Deprecation Toolkit](https://yomu.fyi/post/introducing-the-deprecation-toolkit.md)
- Company: [Shopify](https://yomu.fyi/company/shopify.md)
- Author: Edouard CHIN
- Published: Sep 19, 2018
- Type: Announcement

Shopify’s Deprecation Toolkit is an open-source Ruby gem for tracking deprecated code in an application and preventing new deprecations from entering a codebase. It addresses the limits of warning messages and the impracticality of immediately raising on every existing deprecation, especially in a large monolith with complex dependency upgrades. The toolkit records deprecations triggered by test suites in YAML whitelist files, then compares later test runs with the accepted set; mismatches trigger configurable behavior, raising an error by default. Existing deprecations can therefore remain acceptable while teams fix them gradually, whereas introduced or removed deprecations are surfaced in CI. Shopify reports that using the toolkit reduced deprecations, supported dependency upgrades, blocked pull requests introducing new ones, and enabled team-based tracking of remediation progress.


[Newer posts](https://yomu.fyi/topic/refactoring.md)
