{"assignment":{"_schema_version":2,"course_id":37,"date_created":"2025-08-17T17:31:53.820837+00:00","date_modified":"2025-09-09T21:50:05.838645+00:00","extra_instructor_files":"","extra_starting_files":"","forked_id":null,"forked_version":null,"hidden":false,"id":2467,"instructions":"## Decomposition\n\n-   Decomposition: The process of breaking down a complex problem or system into smaller, more manageable parts.\n\nWe have previously discussed Decomposition as a prime tactic for managing complexity in code.\nDecomposition is the process of breaking down a complex problem or system into smaller, more manageable parts.\n\nYou don't ever _have_ to decompose. Strictly speaking, functions are not necessary.\nWhen code is executed internally at the deepest layer of the system, it is all just a series of instructions being executed in order.\nThere was a period of time in programming where all code was written in a single monolithic block, without any functions or decomposition.\n\nThis was a nightmare, and seriously prevented people from writing programs at scale.\nIt is simply a fact that human minds have finite mental windows, and can only handle so much complexity at size.\nDecomposition is a defensive mechanism that makes your life easier.\n\n## De-decomposing (Inlining)\n\n```python de-decomposing\n@route\ndef submit_guess(state: State, guess: str) -> Page:\n    guess = guess.lower().strip()\n    if not (not len(guess) > 1 and guess in printable and state.guesses not in guess):\n        return play_level(state)\n\n    state.guesses.append(guess)\n    if not (guess in state.secret):\n        state.wrong += 1\n\n    is_win = True\n    if state.secret[0] not in state.guesses:\n        is_win = False\n    if state.secret[1] not in state.guesses:\n        is_win = False\n    if state.secret[2] not in state.guesses:\n        is_win = False\n    if state.secret[3] not in state.guesses:\n        is_win = False\n\n    if is_win:\n        return win_level(state)\n\n    if state.wrong >= 6:\n        return lose_level(state)\n\n    return play_level(state)\n```\n\nSometimes, often for performance reasons, decomposition is reversed, and the code that was removed to a function is placed back into the main body of the code.\nThis is known as **inlining**.\nFor low-level languages, this can sometimes lead to a performance boost, since it reduces the overhead of function calls.\nHowever, most of the time, this boost is so negligible that it is not worth the loss of readability and maintainability that comes with inlining.\n\nIn the code shown here, we have created a new version of `submit_guess` that inlines the logic of all the helper functions. We have left the route function calls intact, but you can imagine that they could also be inlined.\nConsidering each of those functions is a dozen or more lines of code, that would make the overall length of code almost twice as long!\nAlthough some folks might be fine reading this, we believe that it makes the code harder to understand.\n\nOn the other hand, some of our helper functions are quite small and simple, and inlining them might not have a significant impact on readability. The `is_game_over` and `in_secret` functions in particular are good candidates for inlining, as they are both short and self-contained.\nIt is a matter of personal taste and experience to decide when it is worth inlining vs. decomposing.\n\n## Revealing the Error in `reveal`\n\n```python bad-reveal\ndef reveal(secret: str, guesses: list[str]) -> str:\n    if secret[0] in guesses:\n        c0 = secret[0]\n    else:\n        c0 = \"_\"\n    if secret[1] in guesses:\n        c1 = secret[1]\n    else:\n        c1 = \"_\"\n    if secret[2] in guesses:\n        c2 = secret[1]\n    else:\n        c2 = \"_\"\n    if secret[3] in guesses:\n        c3 = secret[3]\n    else:\n        c3 = \"_\"\n    return c0 + c1 + c2 + c3\n```\n\nThat said, a real benefit of inlining complex code is that it can help you identify errors you didn't even know that you had.\nConsider the code shown here for the `reveal` function.\nAstute readers might have already noticed that it has a subtle mistake: the third `if` statement checks `secret[2]` against `guesses`, but it assigns `c2` to `secret[1]` instead of `secret[2]`.\nMost likely, when the developer was writing this code block, they were repeatedly copy-pasting the same thing, and didn't notice the minor typo.\nIf there was less code to read, they might have noticed the mistake.\nThat's why this code block is a prime candidate for decomposition.\nWe can extract the repeated `if` check into a separate helper function, although we will need to vary the index checked each time.\n\n## Improving `reveal`\n\n```python good-reveal\ndef reveal_letter(secret: str, guesses: list[str], index: int) -> str:\n    if secret[index] in guesses:\n        return secret[index]\n    return \"_\"\n\ndef reveal(secret: str, guesses: list[str]) -> str:\n    c0 = reveal_letter(secret, guesses, 0)\n    c1 = reveal_letter(secret, guesses, 1)\n    c2 = reveal_letter(secret, guesses, 2)\n    c3 = reveal_letter(secret, guesses, 3)\n    return c0 + c1 + c2 + c3\n```\n\nIn our new version of `reveal` shown here, we use a helper function named `reveal_letter` to encapsulate the logic for revealing a single letter in the secret word. This makes the `reveal` function much cleaner and easier to read.\nIf we had written `2` instead of `1` for the third call, we are much more likely to notice the mistake.\n\nA downside to this extraction was that we had to introduce a new variable (`index`) as a parameter for `reveal_letter`, since the index varies each time.\nThis is not purely unfortunate, because it clarifies the purpose of the previously magic variable.\nHowever, it is more characters that have to be read and interpreted.\nThese tradeoffs must always be considered when decomposing.\nIf the number of new parameters adds more complexity than it removes, you might decide to keep the code more monolithic.\n\nIn the case of `reveal`, we believe that the added complexity is absolutely worth the tradeoff of a new function, so we will use this version going forward.\n\n## Summary\n\n-   Decomposition is a tool for managing complexity in code by breaking down a complex problem into smaller, more manageable parts (usually functions).\n-   The opposite of decomposition is inlining, where code from a helper function is reintroduced into the main function.\n-   Decomposition is not strictly necessary, but can help identify errors and make code more maintainable.\n","ip_ranges":"","name":"5B1) Decomposition","on_change":"","on_eval":"","on_run":"","owner_id":1,"owner_id__email":"acbart@udel.edu","points":1,"public":true,"reviewed":false,"sample_submissions":[],"settings":"{\n  \"small_layout\": true,\n  \"header\": \"Decomposition\"\n}","starting_code":"","subordinate":false,"tags":[],"type":"reading","url":"bakery_projects_decomposition_read","version":3},"ip":"216.73.216.157","submission":{"_schema_version":3,"assignment_id":2467,"assignment_version":3,"attempts":0,"code":"","correct":false,"course_id":37,"date_created":"2026-05-20T12:38:54.852584+00:00","date_due":"","date_graded":"","date_locked":"","date_modified":"2026-05-20T12:38:54.852584+00:00","date_started":"","date_submitted":"","endpoint":"","extra_files":"","feedback":"","grading_status":"NotReady","id":2036732,"score":0.0,"submission_status":"Started","time_limit":"","url":"submission_url-76f7b791-e656-4b99-8f26-3c2e224e88ab","user_id":2044658,"user_id__email":"","version":0},"success":true}
