{
    "archive_path": "archive/1765537205.109396",
    "base_url": "google.github.io/eng-practices/review/developer/small-cls.html",
    "basename": "small-cls.html",
    "bookmarked_date": "2025-12-12 11:00",
    "canonical": {
        "archive_org_path": "https://web.archive.org/web/google.github.io/eng-practices/review/developer/small-cls.html",
        "dom_path": "output.html",
        "favicon_path": "favicon.ico",
        "git_path": "git/",
        "google_favicon_path": "https://www.google.com/s2/favicons?domain=google.github.io",
        "headers_path": "headers.json",
        "htmltotext_path": "htmltotext.txt",
        "index_path": "index.html",
        "media_path": "media/",
        "mercury_path": "mercury/content.html",
        "pdf_path": "output.pdf",
        "readability_path": "readability/content.html",
        "screenshot_path": "screenshot.png",
        "singlefile_path": "singlefile.html",
        "warc_path": "warc/",
        "wget_path": null
    },
    "domain": "google.github.io",
    "downloaded_at": "2025-12-12T11:00:09.109185+00:00",
    "downloaded_datestr": "2025-12-12 11:00",
    "extension": "html",
    "hash": "1AJRMAYCDQ71A9FA7557",
    "history": {
        "archive_org": [
            {
                "cmd": [
                    "/usr/bin/curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--proxy",
                    "socks5://tor-socks-proxy:9150",
                    "--head",
                    "--max-time",
                    "60",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 ArchiveBox/{VERSION} (+https://github.com/ArchiveBox/ArchiveBox/)",
                    "https://web.archive.org/save/https://google.github.io/eng-practices/review/developer/small-cls.html"
                ],
                "cmd_version": "8.10.1",
                "end_ts": "2025-12-12T11:01:11.284744+00:00",
                "index_texts": null,
                "output": "https://web.archive.org/web/20251212110054/https://google.github.io/eng-practices/review/developer/small-cls.html",
                "pwd": "/data/archive/1765537205.109396",
                "schema": "ArchiveResult",
                "start_ts": "2025-12-12T11:00:48.919246+00:00",
                "status": "succeeded"
            }
        ],
        "dom": [
            {
                "cmd": [
                    "/usr/bin/chromium-browser",
                    "--proxy-server=socks5://tor-socks-proxy:9150",
                    "--disable-features=DarkMode",
                    "--run-all-compositor-stages-before-draw",
                    "--hide-scrollbars",
                    "--autoplay-policy=no-user-gesture-required",
                    "--no-first-run",
                    "--use-fake-ui-for-media-stream",
                    "--use-fake-device-for-media-stream",
                    "--simulate-outdated-no-au='Tue, 31 Dec 2099 23:59:59 GMT'",
                    "--headless=new",
                    "--no-sandbox",
                    "--no-zygote",
                    "--disable-dev-shm-usage",
                    "--disable-software-rasterizer",
                    "--disable-sync",
                    "--window-size=1440,2000",
                    "--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 ArchiveBox/{VERSION} (+https://github.com/ArchiveBox/ArchiveBox/)",
                    "--user-data-dir=/data/personas/Default/chrome_profile",
                    "--profile-directory=Default",
                    "--dump-dom",
                    "https://google.github.io/eng-practices/review/developer/small-cls.html"
                ],
                "cmd_version": "131.0.6778",
                "end_ts": "2025-12-12T11:00:23.044103+00:00",
                "index_texts": null,
                "output": "output.html",
                "pwd": "/data/archive/1765537205.109396",
                "schema": "ArchiveResult",
                "start_ts": "2025-12-12T11:00:17.891146+00:00",
                "status": "succeeded"
            }
        ],
        "favicon": [
            {
                "cmd": [
                    "/usr/bin/curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--proxy",
                    "socks5://tor-socks-proxy:9150",
                    "--max-time",
                    "60",
                    "--output",
                    "favicon.ico",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 ArchiveBox/{VERSION} (+https://github.com/ArchiveBox/ArchiveBox/)",
                    "https://www.google.com/s2/favicons?domain=google.github.io"
                ],
                "cmd_version": "8.10.1",
                "end_ts": "2025-12-12T11:00:13.332765+00:00",
                "index_texts": null,
                "output": "favicon.ico",
                "pwd": "/data/archive/1765537205.109396",
                "schema": "ArchiveResult",
                "start_ts": "2025-12-12T11:00:10.159566+00:00",
                "status": "succeeded"
            }
        ],
        "git": [],
        "headers": [
            {
                "cmd": [
                    "/usr/bin/curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--proxy",
                    "socks5://tor-socks-proxy:9150",
                    "--head",
                    "--max-time",
                    "60",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 ArchiveBox/{VERSION} (+https://github.com/ArchiveBox/ArchiveBox/)",
                    "https://google.github.io/eng-practices/review/developer/small-cls.html"
                ],
                "cmd_version": "8.10.1",
                "end_ts": "2025-12-12T11:00:13.613884+00:00",
                "index_texts": null,
                "output": "headers.json",
                "pwd": "/data/archive/1765537205.109396",
                "schema": "ArchiveResult",
                "start_ts": "2025-12-12T11:00:13.360316+00:00",
                "status": "succeeded"
            }
        ],
        "htmltotext": [
            {
                "cmd": [
                    "(internal) archivebox.extractors.htmltotext",
                    "./{singlefile,dom}.html"
                ],
                "cmd_version": "0.8.5rc51",
                "end_ts": "2025-12-12T11:00:39.292905+00:00",
                "index_texts": [
                    "Small CLs | eng-practices (https://google.github.io/eng-practices/review/developer/small-cls.html) (/eng-practices/assets/css/style.css?v=3bb3ec25b3b0199f4940b1aa75f0ac5c5753301c)  (https://google.github.io/eng-practices/) eng-practices  Small CLs Why Write Small CLs?  Small, simple CLs are: Reviewed more quickly. It\u2019s easier for a reviewer to find five minutes\nseveral times to review small CLs than to set aside a 30 minute block to\nreview one large CL. Reviewed more thoroughly. With large changes, reviewers and authors tend\nto get frustrated by large volumes of detailed commentary shifting back and\nforth\u2014sometimes to the point where important points get missed or dropped. Less likely to introduce bugs. Since you\u2019re making fewer changes, it\u2019s\neasier for you and your reviewer to reason effectively about the impact of\nthe CL and see if a bug has been introduced. Less wasted work if they are rejected. If you write a huge CL and then\nyour reviewer says that the overall direction is wrong, you\u2019ve wasted a lot\nof work. Easier to merge. Working on a large CL takes a long time, so you will\nhave lots of conflicts when you merge, and you will have to merge\nfrequently. Easier to design well. It\u2019s a lot easier to polish the design and code\nhealth of a small change than it is to refine all the details of a large\nchange. Less blocking on reviews. Sending self-contained portions of your\noverall change allows you to continue coding while you wait for your current\nCL in review. Simpler to roll back. A large CL will more likely touch files that get\nupdated between the initial CL submission and a rollback CL, complicating\nthe rollback (the intermediate CLs will probably need to be rolled back\ntoo).  Note that reviewers have discretion to reject your change outright for the\nsole reason of it being too large. Usually they will thank you for your\ncontribution but request that you somehow make it into a series of smaller\nchanges. It can be a lot of work to split up a change after you\u2019ve already\nwritten it, or require lots of time arguing about why the reviewer should accept\nyour large change. It\u2019s easier to just write small CLs in the first place. What is Small?    In general, the right size for a CL is one self-contained change . This means\nthat: The CL makes a minimal change that addresses just one thing . This is\nusually just one part of a feature, rather than a whole feature at once. In\ngeneral it\u2019s better to err on the side of writing CLs that are too small vs.\nCLs that are too large. Work with your reviewer to find out what an\nacceptable size is. The CL should include related test code . Everything the reviewer needs to understand about the CL (except future\ndevelopment) is in the CL, the CL\u2019s description, the existing codebase, or a\nCL they\u2019ve already reviewed. The system will continue to work well for its users and for the developers\nafter the CL is checked in. The CL is not so small that its implications are difficult to understand. If\nyou add a new API, you should include a usage of the API in the same CL so\nthat reviewers can better understand how the API will be used. This also\nprevents checking in unused APIs.  There are no hard and fast rules about how large is \u201ctoo large.\u201d 100 lines is\nusually a reasonable size for a CL, and 1000 lines is usually too large, but\nit\u2019s up to the judgment of your reviewer. The number of files that a change is\nspread across also affects its \u201csize.\u201d A 200-line change in one file might be\nokay, but spread across 50 files it would usually be too large. Keep in mind that although you have been intimately involved with your code from\nthe moment you started to write it, the reviewer often has no context. What\nseems like an acceptably-sized CL to you might be overwhelming to your reviewer.\nWhen in doubt, write CLs that are smaller than you think you need to write.\nReviewers rarely complain about getting CLs that are too small. When are Large CLs Okay?    There are a few situations in which large changes aren\u2019t as bad: You can usually count deletion of an entire file as being just one line of\nchange, because it doesn\u2019t take the reviewer very long to review. Sometimes a large CL has been generated by an automatic refactoring tool\nthat you trust completely, and the reviewer\u2019s job is just to verify and say\nthat they really do want the change. These CLs can be larger, although some\nof the caveats from above (such as merging and testing) still apply.  Writing Small CLs Efficiently  If you write a small CL and then you wait for your reviewer to approve it before\nyou write your next CL, then you\u2019re going to waste a lot of time. So you want to\nfind some way to work that won\u2019t block you while you\u2019re waiting for review. This\ncould involve having multiple projects to work on simultaneously, finding\nreviewers who agree to be immediately available, doing in-person reviews, pair\nprogramming, or splitting your CLs in a way that allows you to continue working\nimmediately. Splitting CLs  When starting work that will have multiple CLs with potential dependencies among\neach other, it\u2019s often useful to think about how to split and organize those CLs\nat a high level before diving into coding. Besides making things easier for you as an author to manage and organize your\nCLs, it also makes things easier for your code reviewers, which in turn makes\nyour code reviews more efficient. Here are some strategies for splitting work into different CLs. Stacking Multiple Changes on Top of Each Other  One way to split up a CL without blocking yourself is to write one small CL,\nsend it off for review, and then immediately start writing another CL based on\nthe first CL. Most version control systems allow you to do this somehow. Splitting by Files  Another way to split up a CL is by groupings of files that will require\ndifferent reviewers but are otherwise self-contained changes. For example: you send off one CL for modifications to a protocol buffer and\nanother CL for changes to the code that uses that proto. You have to submit the\nproto CL before the code CL, but they can both be reviewed simultaneously. If\nyou do this, you might want to inform both sets of reviewers about the other CL\nthat you wrote, so that they have context for your changes. Another example: you send one CL for a code change and another for the\nconfiguration or experiment that uses that code; this is easier to roll back\ntoo, if necessary, as configuration/experiment files are sometimes pushed to\nproduction faster than code changes. Splitting Horizontally  Consider creating shared code or stubs that help isolate changes between layers\nof the tech stack. This not only helps expedite development but also encourages\nabstraction between layers. For example: You created a calculator app with client, API, service, and data\nmodel layers. A shared proto signature can abstract the service and data model\nlayers from each other. Similarly, an API stub can split the implementation of\nclient code from service code and enable them to move forward independently.\nSimilar ideas can also be applied to more granular function or class level\nabstractions. Splitting Vertically  Orthogonal to the layered, horizontal approach, you can instead break down your\ncode into smaller, full-stack, vertical features. Each of these features can be\nindependent parallel implementation tracks. This enables some tracks to move\nforward while other tracks are awaiting review or feedback. Back to our calculator example from Splitting Horizontally . You now want to support new\noperators, like multiplication and division. You could split this up by\nimplementing multiplication and division as separate verticals or sub-features,\neven though they may have some overlap such as shared button styling or shared\nvalidation logic. Splitting Horizontally & Vertically  To take this a step further, you could combine these approaches and chart out an\nimplementation plan like this, where each cell is its own standalone CL.\nStarting from the model (at the bottom) and working up to the client: | Layer   | Feature: Multiplication   | Feature: Division               | | \u2014\u2014- | \u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014- | \u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014- | | Client  | Add button                | Add button                      | | API     | Add endpoint              | Add endpoint                    | | Service | Implement transformations | Share transformation logic with | :                           : multiplication                  :\n| Model   | Add proto definition      | Add proto definition            |  Separate Out Refactorings  It\u2019s usually best to do refactorings in a separate CL from feature changes or\nbug fixes. For example, moving and renaming a class should be in a different CL\nfrom fixing a bug in that class. It is much easier for reviewers to understand\nthe changes introduced by each CL when they are separate. Small cleanups such as fixing a local variable name can be included inside of a\nfeature change or bug fix CL, though. It\u2019s up to the judgment of developers and\nreviewers to decide when a refactoring is so large that it will make the review\nmore difficult if included in your current CL. Keep related test code in the same CL    CLs should include related test code. Remember that smallness here refers the conceptual idea that the CL should be focused and is not a\nsimplistic function on line count. Tests are expected for all Google changes. A CL that adds or changes logic should be accompanied by new or updated tests\nfor the new behavior. Pure refactoring CLs (that aren\u2019t intended to change\nbehavior) should also be covered by tests; ideally, these tests already exist,\nbut if they don\u2019t, you should add them. Independent test modifications can go into separate CLs first, similar to the refactorings guidelines . That includes: Validating pre-existing, submitted code with new tests. Ensures that important logic is covered by tests. Increases confidence in subsequent refactorings on affected code. For\nexample, if you want to refactor code that isn\u2019t already covered by\ntests, submitting test CLs before submitting refactoring CLs can\nvalidate that the tested behavior is unchanged before and after the\nrefactoring.   Refactoring the test code (e.g. introduce helper functions). Introducing larger test framework code (e.g. an integration test).  Don\u2019t Break the Build  If you have several CLs that depend on each other, you need to find a way to\nmake sure the whole system keeps working after each CL is submitted. Otherwise\nyou might break the build for all your fellow developers for a few minutes\nbetween your CL submissions (or even longer if something goes wrong unexpectedly\nwith your later CL submissions). Can\u2019t Make it Small Enough  Sometimes you will encounter situations where it seems like your CL has to be\nlarge. This is very rarely true. Authors who practice writing small CLs can\nalmost always find a way to decompose functionality into a series of small\nchanges. Before writing a large CL, consider whether preceding it with a refactoring-only\nCL could pave the way for a cleaner implementation. Talk to your teammates and\nsee if anybody has thoughts on how to implement the functionality in small CLs\ninstead. If all of these options fail (which should be extremely rare) then get consent\nfrom your reviewers in advance to review a large CL, so they are warned about\nwhat is coming. In this situation, expect to be going through the review process\nfor a long time, be vigilant about not introducing bugs, and be extra diligent\nabout writing tests. Next: (/eng-practices/review/developer/handling-comments.html) How to Handle Reviewer Comments  This site is open source. (https://github.com/google/eng-practices/edit/master/review/developer/small-cls.md) Improve this page .     "
                ],
                "output": "htmltotext.txt",
                "pwd": "/data/archive/1765537205.109396",
                "schema": "ArchiveResult",
                "start_ts": "2025-12-12T11:00:39.277317+00:00",
                "status": "succeeded"
            }
        ],
        "media": [
            {
                "cmd": [
                    "/usr/local/bin/yt-dlp",
                    "--restrict-filenames",
                    "--trim-filenames",
                    "128",
                    "--write-description",
                    "--write-info-json",
                    "--write-annotations",
                    "--write-thumbnail",
                    "--no-call-home",
                    "--write-sub",
                    "--write-auto-subs",
                    "--convert-subs=srt",
                    "--yes-playlist",
                    "--continue",
                    "--no-abort-on-error",
                    "--ignore-errors",
                    "--geo-bypass",
                    "--add-metadata",
                    "--format=(bv*+ba/b)[filesize<=750m][filesize_approx<=?750m]/(bv*+ba/b)",
                    "--skip-download",
                    "--cache-dir=/data/yt-dlp-cache/",
                    "--cookies=/data/yt-dlp-cache/cookies.txt",
                    "--proxy=socks5://tor-socks-proxy:9150",
                    "--no-playlist",
                    "https://google.github.io/eng-practices/review/developer/small-cls.html"
                ],
                "cmd_version": "2024.10.7",
                "end_ts": "2025-12-12T11:00:48.867122+00:00",
                "index_texts": [],
                "output": "media/",
                "pwd": "/data/archive/1765537205.109396",
                "schema": "ArchiveResult",
                "start_ts": "2025-12-12T11:00:42.438393+00:00",
                "status": "succeeded"
            }
        ],
        "mercury": [
            {
                "cmd": [
                    "/home/archivebox/.npm/bin/postlight-parser",
                    "https://google.github.io/eng-practices/review/developer/small-cls.html"
                ],
                "cmd_version": "2.2.3",
                "end_ts": "2025-12-12T11:00:39.252095+00:00",
                "index_texts": null,
                "output": "mercury/",
                "pwd": "/data/archive/1765537205.109396",
                "schema": "ArchiveResult",
                "start_ts": "2025-12-12T11:00:37.255083+00:00",
                "status": "succeeded"
            }
        ],
        "pdf": [],
        "readability": [
            {
                "cmd": [
                    "/home/archivebox/.npm/bin/readability-extractor",
                    "/tmp/tmpfl1wge7z",
                    "https://google.github.io/eng-practices/review/developer/small-cls.html"
                ],
                "cmd_version": "0.0.11",
                "end_ts": "2025-12-12T11:00:25.422873+00:00",
                "index_texts": [
                    "eng-practices\n      \n\n      Small CLs\n\nWhy Write Small CLs?\n\nSmall, simple CLs are:\n\n\n  Reviewed more quickly. It\u2019s easier for a reviewer to find five minutes\nseveral times to review small CLs than to set aside a 30 minute block to\nreview one large CL.\n  Reviewed more thoroughly. With large changes, reviewers and authors tend\nto get frustrated by large volumes of detailed commentary shifting back and\nforth\u2014sometimes to the point where important points get missed or dropped.\n  Less likely to introduce bugs. Since you\u2019re making fewer changes, it\u2019s\neasier for you and your reviewer to reason effectively about the impact of\nthe CL and see if a bug has been introduced.\n  Less wasted work if they are rejected. If you write a huge CL and then\nyour reviewer says that the overall direction is wrong, you\u2019ve wasted a lot\nof work.\n  Easier to merge. Working on a large CL takes a long time, so you will\nhave lots of conflicts when you merge, and you will have to merge\nfrequently.\n  Easier to design well. It\u2019s a lot easier to polish the design and code\nhealth of a small change than it is to refine all the details of a large\nchange.\n  Less blocking on reviews. Sending self-contained portions of your\noverall change allows you to continue coding while you wait for your current\nCL in review.\n  Simpler to roll back. A large CL will more likely touch files that get\nupdated between the initial CL submission and a rollback CL, complicating\nthe rollback (the intermediate CLs will probably need to be rolled back\ntoo).\n\n\nNote that reviewers have discretion to reject your change outright for the\nsole reason of it being too large. Usually they will thank you for your\ncontribution but request that you somehow make it into a series of smaller\nchanges. It can be a lot of work to split up a change after you\u2019ve already\nwritten it, or require lots of time arguing about why the reviewer should accept\nyour large change. It\u2019s easier to just write small CLs in the first place.\n\nWhat is Small?\n\n\n\nIn general, the right size for a CL is one self-contained change. This means\nthat:\n\n\n  The CL makes a minimal change that addresses just one thing. This is\nusually just one part of a feature, rather than a whole feature at once. In\ngeneral it\u2019s better to err on the side of writing CLs that are too small vs.\nCLs that are too large. Work with your reviewer to find out what an\nacceptable size is.\n  The CL should include related test code.\n  Everything the reviewer needs to understand about the CL (except future\ndevelopment) is in the CL, the CL\u2019s description, the existing codebase, or a\nCL they\u2019ve already reviewed.\n  The system will continue to work well for its users and for the developers\nafter the CL is checked in.\n  The CL is not so small that its implications are difficult to understand. If\nyou add a new API, you should include a usage of the API in the same CL so\nthat reviewers can better understand how the API will be used. This also\nprevents checking in unused APIs.\n\n\nThere are no hard and fast rules about how large is \u201ctoo large.\u201d 100 lines is\nusually a reasonable size for a CL, and 1000 lines is usually too large, but\nit\u2019s up to the judgment of your reviewer. The number of files that a change is\nspread across also affects its \u201csize.\u201d A 200-line change in one file might be\nokay, but spread across 50 files it would usually be too large.\n\nKeep in mind that although you have been intimately involved with your code from\nthe moment you started to write it, the reviewer often has no context. What\nseems like an acceptably-sized CL to you might be overwhelming to your reviewer.\nWhen in doubt, write CLs that are smaller than you think you need to write.\nReviewers rarely complain about getting CLs that are too small.\n\nWhen are Large CLs Okay?\n\n\n\nThere are a few situations in which large changes aren\u2019t as bad:\n\n\n  You can usually count deletion of an entire file as being just one line of\nchange, because it doesn\u2019t take the reviewer very long to review.\n  Sometimes a large CL has been generated by an automatic refactoring tool\nthat you trust completely, and the reviewer\u2019s job is just to verify and say\nthat they really do want the change. These CLs can be larger, although some\nof the caveats from above (such as merging and testing) still apply.\n\n\nWriting Small CLs Efficiently\n\nIf you write a small CL and then you wait for your reviewer to approve it before\nyou write your next CL, then you\u2019re going to waste a lot of time. So you want to\nfind some way to work that won\u2019t block you while you\u2019re waiting for review. This\ncould involve having multiple projects to work on simultaneously, finding\nreviewers who agree to be immediately available, doing in-person reviews, pair\nprogramming, or splitting your CLs in a way that allows you to continue working\nimmediately.\n\nSplitting CLs\n\nWhen starting work that will have multiple CLs with potential dependencies among\neach other, it\u2019s often useful to think about how to split and organize those CLs\nat a high level before diving into coding.\n\nBesides making things easier for you as an author to manage and organize your\nCLs, it also makes things easier for your code reviewers, which in turn makes\nyour code reviews more efficient.\n\nHere are some strategies for splitting work into different CLs.\n\nStacking Multiple Changes on Top of Each Other\n\nOne way to split up a CL without blocking yourself is to write one small CL,\nsend it off for review, and then immediately start writing another CL based on\nthe first CL. Most version control systems allow you to do this somehow.\n\nSplitting by Files\n\nAnother way to split up a CL is by groupings of files that will require\ndifferent reviewers but are otherwise self-contained changes.\n\nFor example: you send off one CL for modifications to a protocol buffer and\nanother CL for changes to the code that uses that proto. You have to submit the\nproto CL before the code CL, but they can both be reviewed simultaneously. If\nyou do this, you might want to inform both sets of reviewers about the other CL\nthat you wrote, so that they have context for your changes.\n\nAnother example: you send one CL for a code change and another for the\nconfiguration or experiment that uses that code; this is easier to roll back\ntoo, if necessary, as configuration/experiment files are sometimes pushed to\nproduction faster than code changes.\n\nSplitting Horizontally\n\nConsider creating shared code or stubs that help isolate changes between layers\nof the tech stack. This not only helps expedite development but also encourages\nabstraction between layers.\n\nFor example: You created a calculator app with client, API, service, and data\nmodel layers. A shared proto signature can abstract the service and data model\nlayers from each other. Similarly, an API stub can split the implementation of\nclient code from service code and enable them to move forward independently.\nSimilar ideas can also be applied to more granular function or class level\nabstractions.\n\nSplitting Vertically\n\nOrthogonal to the layered, horizontal approach, you can instead break down your\ncode into smaller, full-stack, vertical features. Each of these features can be\nindependent parallel implementation tracks. This enables some tracks to move\nforward while other tracks are awaiting review or feedback.\n\nBack to our calculator example from\nSplitting Horizontally. You now want to support new\noperators, like multiplication and division. You could split this up by\nimplementing multiplication and division as separate verticals or sub-features,\neven though they may have some overlap such as shared button styling or shared\nvalidation logic.\n\nSplitting Horizontally & Vertically\n\nTo take this a step further, you could combine these approaches and chart out an\nimplementation plan like this, where each cell is its own standalone CL.\nStarting from the model (at the bottom) and working up to the client:\n\n\n  | Layer   | Feature: Multiplication   | Feature: Division               |\n  | \u2014\u2014- | \u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014- | \u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014- |\n  | Client  | Add button                | Add button                      |\n  | API     | Add endpoint              | Add endpoint                    |\n  | Service | Implement transformations | Share transformation logic with |\n  :                           : multiplication                  :\n| Model   | Add proto definition      | Add proto definition            |\n\n\nSeparate Out Refactorings\n\nIt\u2019s usually best to do refactorings in a separate CL from feature changes or\nbug fixes. For example, moving and renaming a class should be in a different CL\nfrom fixing a bug in that class. It is much easier for reviewers to understand\nthe changes introduced by each CL when they are separate.\n\nSmall cleanups such as fixing a local variable name can be included inside of a\nfeature change or bug fix CL, though. It\u2019s up to the judgment of developers and\nreviewers to decide when a refactoring is so large that it will make the review\nmore difficult if included in your current CL.\n\nKeep related test code in the same CL\n\n\n\nCLs should include related test code. Remember that smallness\nhere refers the conceptual idea that the CL should be focused and is not a\nsimplistic function on line count.\n\nTests are expected for all Google changes.\n\nA CL that adds or changes logic should be accompanied by new or updated tests\nfor the new behavior. Pure refactoring CLs (that aren\u2019t intended to change\nbehavior) should also be covered by tests; ideally, these tests already exist,\nbut if they don\u2019t, you should add them.\n\nIndependent test modifications can go into separate CLs first, similar to the\nrefactorings guidelines. That includes:\n\n\n  Validating pre-existing, submitted code with new tests.\n    \n      Ensures that important logic is covered by tests.\n      Increases confidence in subsequent refactorings on affected code. For\nexample, if you want to refactor code that isn\u2019t already covered by\ntests, submitting test CLs before submitting refactoring CLs can\nvalidate that the tested behavior is unchanged before and after the\nrefactoring.\n    \n  \n  Refactoring the test code (e.g. introduce helper functions).\n  Introducing larger test framework code (e.g. an integration test).\n\n\nDon\u2019t Break the Build\n\nIf you have several CLs that depend on each other, you need to find a way to\nmake sure the whole system keeps working after each CL is submitted. Otherwise\nyou might break the build for all your fellow developers for a few minutes\nbetween your CL submissions (or even longer if something goes wrong unexpectedly\nwith your later CL submissions).\n\nCan\u2019t Make it Small Enough\n\nSometimes you will encounter situations where it seems like your CL has to be\nlarge. This is very rarely true. Authors who practice writing small CLs can\nalmost always find a way to decompose functionality into a series of small\nchanges.\n\nBefore writing a large CL, consider whether preceding it with a refactoring-only\nCL could pave the way for a cleaner implementation. Talk to your teammates and\nsee if anybody has thoughts on how to implement the functionality in small CLs\ninstead.\n\nIf all of these options fail (which should be extremely rare) then get consent\nfrom your reviewers in advance to review a large CL, so they are warned about\nwhat is coming. In this situation, expect to be going through the review process\nfor a long time, be vigilant about not introducing bugs, and be extra diligent\nabout writing tests.\n\nNext: How to Handle Reviewer Comments"
                ],
                "output": "readability/",
                "pwd": "/data/archive/1765537205.109396",
                "schema": "ArchiveResult",
                "start_ts": "2025-12-12T11:00:23.794259+00:00",
                "status": "succeeded"
            }
        ],
        "screenshot": [],
        "singlefile": [],
        "title": [
            {
                "cmd": [
                    "/usr/bin/curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--proxy",
                    "socks5://tor-socks-proxy:9150",
                    "--max-time",
                    "60",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 ArchiveBox/{VERSION} (+https://github.com/ArchiveBox/ArchiveBox/)",
                    "https://google.github.io/eng-practices/review/developer/small-cls.html"
                ],
                "cmd_version": "8.10.1",
                "end_ts": "2025-12-12T11:00:23.127897+00:00",
                "index_texts": null,
                "output": "Small CLs | eng-practices",
                "pwd": "/data/archive/1765537205.109396",
                "schema": "ArchiveResult",
                "start_ts": "2025-12-12T11:00:23.094804+00:00",
                "status": "succeeded"
            }
        ],
        "wget": []
    },
    "icons": null,
    "is_archived": true,
    "is_static": false,
    "latest": {
        "archive_org": "https://web.archive.org/web/20251212110054/https://google.github.io/eng-practices/review/developer/small-cls.html",
        "dom": "output.html",
        "favicon": "favicon.ico",
        "git": null,
        "media": "media/",
        "pdf": null,
        "screenshot": null,
        "singlefile": null,
        "title": "Small CLs | eng-practices",
        "warc": null,
        "wget": null
    },
    "link_dir": "/data/archive/1765537205.109396",
    "newest_archive_date": "2025-12-12T11:00:48.919246+00:00",
    "num_failures": 0,
    "num_outputs": 9,
    "oldest_archive_date": "2025-12-12T11:00:10.159566+00:00",
    "path": "/eng-practices/review/developer/small-cls.html",
    "schema": "Link",
    "scheme": "https",
    "snapshot_abid": "snp_01KC93FJXE6B897C46013XZJRW",
    "snapshot_id": "f8a7bf26-3126-4600-bb6a-876ac7dfcb1c",
    "sources": [
        "/data/sources/1765537204-import.txt"
    ],
    "tags": null,
    "tags_str": "",
    "timestamp": "1765537205.109396",
    "title": "Small CLs | eng-practices",
    "url": "https://google.github.io/eng-practices/review/developer/small-cls.html"
}