Parallelization

2 posts

slack3 min readCurated summary

Build better software to build software better

Slack’s backend build pipeline for Quip and Slack Canvas once took 60 minutes, delaying feedback and slowing delivery. The team improved build performance by applying familiar software-engineering techniques—caching, parallelization, precise interfaces, and careful decomposition—using Bazel. The central argument is that build systems should be designed like high-performance programs: do less work, distribute unavoidable work, and define work units rigorously. ## Modeling Builds as Dependency Graphs - Applications can be represented as directed acyclic graphs of source files, intermediate artifacts, and deployable outputs. - A backend artifact depends on Python files, while a frontend artifact depends on TypeScript files. - Changing a Python file should rebuild the backend but not unrelated frontend components. - Clearly defined graph nodes allow build systems to optimize work rather than rebuilding everything. ## Caching and Hermetic Work - Caching avoids repeating expensive operations by storing outputs for known inputs. - The article uses a cached recursive `factorial()` function as an analogy: - The input is the cache key. - The return value is the cached artifact. - Effective caching requires work to be: - **Hermetic:** dependent only on explicitly provided inputs. - **Idempotent:** producing the same output for the same inputs. - Cache hit rate matters: poorly defined work units produce more cache misses. ## Granular Cache Units - Caching an entire `process_images(images, transforms)` operation is inefficient because changing one image invalidates the result for every image. - A more granular design caches `process_image(image, transform)` independently. - The higher-level operation can then reuse cached results and process only new image-transform combinations. - Smaller, well-defined units generally improve cache reuse and reduce rebuild time. ## Parallelizing Independent Work - Image processing can also be distributed across CPU threads using `ThreadPoolExecutor`. - Parallel work requires: - Completely specified inputs and outputs. - The ability to transfer data across thread, process, or network boundaries. - Handling completion and failure in any order. - APIs must document ordering guarantees; the threaded example returns images in completion order rather than input order. - Work-unit granularity affects scalability: - Too few large tasks limit available parallelism. - Too many tiny tasks may introduce coordination overhead. - The appropriate balance depends on the workload. ## Applying These Principles to Bazel - Bazel represents builds as directed acyclic graphs made of targets. - Each target defines: - Its input or dependency files. - Its output files. - The commands that transform inputs into outputs. - This structure provides the foundation for caching and parallel execution, just as explicit function inputs and outputs enable those optimizations in application code. The practical recommendation is to design build steps as small, hermetic, idempotent, and independently executable units. Combined with Bazel’s dependency graph, this lets teams avoid unnecessary work, maximize cache hits, and run independent tasks concurrently—turning slow build pipelines into faster sources of developer feedback.

Read original(opens in new tab)
figma2 min readCurated summary

Keeping Figma Fast | Figma Blog

Figma’s original performance-testing system relied on a single MacBook running a few repeated scenarios, which worked when the product and team were smaller. As Figma expanded into plugins, FigJam, Dev Mode, and many other features, that approach became too limited and fragile—the laptop eventually overheated while the company was remote. Figma concluded it needed a scalable, proactive testing framework capable of catching regressions across its growing codebase. ## From One Laptop to a Growing Product - In 2018, one MacBook repeatedly ran performance scenarios and reported timing changes to a shared dashboard. - A major renderer restructuring and WebAssembly fixes made Figma three times faster. - Over five years, the product grew substantially, adding: - Plugins and Community features - FigJam - Dev Mode - Numerous ongoing product updates - The existing test files could no longer represent the product’s expanding feature set and edge cases. ## Limits of the Original Testing Process - Granular performance tests measure specific behavior at scale, such as panning through a file while 100 users edit layers and type simultaneously. - Creating feature-specific tests became impractical as Figma grew beyond 400 engineers and managers. - Increasing release velocity made it difficult for any individual or single machine to track every performance-affecting change. - During the shift to remote work in 2020, the office MacBook remained running unattended and eventually overheated. - Attempts to reproduce the setup on another laptop were unsuccessful, demonstrating that the system was not operationally scalable. ## Requirements for a New System Figma used the overhaul to define an ideal performance-testing framework: - **Test every proposed code change:** Performance checks should run against changes in the main monorepo, allowing regressions to be found during development rather than after users encounter them. - **Support proactive performance work:** Small delays can significantly disrupt users who spend hours working in Figma. - **Run tests in parallel:** Dozens of stress scenarios would need to execute simultaneously, similar to Figma’s existing cloud-based CI testing. - **Finish quickly:** Performance guardrail checks were required to complete in under 10 minutes. - **Scale across real hardware:** Running every pull request on physical machines could require roughly 100 identical runners at peak capacity. Figma’s experience shows that performance testing must evolve alongside product complexity. A lightweight single-machine setup can be effective initially, but larger teams and faster release cycles require automated, parallel, hardware-aware testing integrated directly into CI.

Read original(opens in new tab)