{"course":{"slug":"how-software-changed","title":"How Software Development Changed","url":"https://addaly.com/learn/how-software-changed","level":"intermediate","track":"work"},"lesson":{"slug":"unfamiliar-language","title":"Shipping in a language you do not know","url":"https://addaly.com/learn/how-software-changed/unfamiliar-language","position":45,"of":87,"minutes":9,"answer":"Generation lets you ship in an unfamiliar language but not maintain or debug in one, and what still bites is the language's error-handling norms, concurrency model and tooling.","keyIdea":"Generation lets you ship in an unfamiliar language but not maintain or debug in one, and what still bites is the language's error-handling norms, concurrency model and tooling, so turn the strictest linter on first and get one fluent human to read the first thing you ship."},"body":{"format":"markdown","text":"## The strongest case, honestly assessed\n\nThis is where the measured gains are largest and where the experience matches the measurement. If you understand the problem and merely cannot write the syntax, the barrier that used to cost a fortnight now costs an afternoon.\n\nA backend engineer who has never written Swift can produce a working iOS screen. A Python developer can write a Go service. Somebody who knows infrastructure but not HCL can write a Terraform module. These are real and they matter, particularly for people whose careers were bounded by which language their first job used.\n\nWhat follows is the list of what still bites, because the failure mode here is specific: the code compiles, the tests pass, and it is wrong in ways that only somebody fluent would see.\n\n## What still bites\n\n**Error handling conventions.** Go returns errors and you check every one; ignoring one with `_` is a code smell that a Python developer will not notice. Rust's `Result` demands handling and `.unwrap()` is a decision to crash. Java distinguishes checked and unchecked exceptions and the convention around each is cultural. Generated code will produce syntactically valid error handling that is wrong for the language's norms, and error handling is where reliability lives.\n\n**Memory and lifetime.** In C and C++, who owns this pointer and when is it freed. In Rust, why the borrow checker is complaining, which is usually because your design is wrong rather than because the compiler is. In JavaScript, why a closure is keeping a large object alive. These are the categories where a plausible answer that compiles can still leak or crash under load.\n\n**Concurrency models are not interchangeable.** Go's goroutines and channels, Java's threads and executors, JavaScript's single-threaded event loop, Python's GIL, Erlang's processes. Code translated in spirit from one to another is a category error, and it will look fine.\n\n**The standard library you do not know.** You will accept a hand-written implementation of something the language already provides, because you do not know it exists. This is the most common form of the problem and the least harmful.\n\n**Build, packaging and tooling.** Gradle, Cargo, Go modules, Maven, CMake, npm workspaces. This is where the days actually go, it is poorly represented in generated answers because it is so project-specific, and it is the part nobody warns you about.\n\n**Idioms that signal competence.** Whether you use a `context.Context` as the first parameter, whether you return early, whether you use a builder. Getting these wrong is not a correctness problem: it is a signal to every future reader that nobody here knew the language, which affects how much they trust the rest.\n\n## A checklist for shipping in a language you do not know\n\n1. **Read the official tour or getting-started guide.** Two hours. Go's Tour, the Rust Book's first four chapters, the Swift language guide. Not to learn the language: to learn what its people care about, which is what generated code will not tell you.\n2. **Set up the linter and formatter first, on their strictest settings.** `golangci-lint`, `clippy`, `ruff`, `eslint` with a recommended config, `ktlint`. These encode the community's accumulated knowledge of what goes wrong, and they are free. In an unfamiliar language they are worth more than in a familiar one, precisely because you cannot yet supply that judgement.\n3. **Find one high-quality open-source project in the language and read it.** Not all of it. One directory. You are calibrating what good looks like.\n4. **Get a fluent human to review the first thing you ship.** One hour of somebody who knows the language will find things no amount of reading will. If you do not have a colleague, open-source communities and forums will often do it for a small, well-posed change.\n5. **Do not choose the architecture.** Follow the framework's documented structure exactly, even where it seems clumsy. You do not yet have the standing to deviate, and deviations in an unfamiliar language compound.\n6. **Write down what you do not understand.** A running list. Work through it over weeks. This is the difference between shipping in a language and learning one, and both are legitimate, but be clear with yourself about which you are doing.\n\n## The honest framing\n\nYou can now ship working software in a language you do not know. You cannot yet maintain a system in it, debug it under pressure, judge whether a design fits it, or review someone else's code in it.\n\nThat is a useful capability with a clear boundary, and the mistake is not using it: the mistake is not noticing where the boundary is. Being one week into a language and shipping to production is fine for a small internal tool and reckless for a payment path, and the difference is entirely about what happens when it goes wrong at a time when you are the only person available."},"check":{"question":"A Python developer ships a Go service. It passes review by other Python developers, compiles, and works. Six weeks later it fails under load in a way nobody can diagnose. Which of the listed gaps most likely explains this?","options":[{"text":"Unfamiliarity with the standard library, so hand-written implementations of built-in functions carry subtle bugs.","correct":false,"tempting":"The most frequent gap, but reimplemented helpers usually fail visibly and early rather than only under load."},{"text":"Missing idioms such as `context.Context` threading, which made the code unrecognisable to Go developers asked to help.","correct":false,"tempting":"Idiom failures damage reviewability and trust, but they do not by themselves produce a load-dependent failure."},{"text":"A goroutine and channel design translated in spirit from Python's threading model.","correct":true},{"text":"Build and packaging differences, since Go's module system and dependency resolution differ substantially from pip's.","correct":false,"tempting":"Tooling costs days of setup time but produces build failures rather than runtime behaviour that only appears at scale."}]},"prev":{"slug":"refactoring-and-codemods","title":"The task it is best at","url":"https://addaly.com/learn/how-software-changed/refactoring-and-codemods"},"next":{"slug":"when-not-to-reach-for-it","title":"When to put the tool down","url":"https://addaly.com/learn/how-software-changed/when-not-to-reach-for-it"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'Shipping in a language you do not know', in How Software Development Changed. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/how-software-changed/unfamiliar-language (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"Shipping in a language you do not know","courseTitle":"How Software Development Changed","module":{"index":5,"slug":"the-working-loop","title":"The working loop"},"answer":null,"keyIdea":"Generation lets you ship in an unfamiliar language but not maintain or debug in one, and what still bites is the language’s error-handling norms, concurrency model and tooling, so turn the strictest linter on first and get one fluent human to read the first thing you ship.","hero":{"kind":"plate","image":null,"alt":null,"caption":null},"blocks":[{"t":"h2","id":"the-strongest-case-honestly-assessed","runs":[{"text":"The strongest case, honestly assessed"}]},{"t":"p","runs":[{"text":"This is where the measured gains are largest and where the experience matches the measurement. If you understand the problem and merely cannot write the syntax, the barrier that used to cost a fortnight now costs an afternoon."}]},{"t":"p","runs":[{"text":"A backend engineer who has never written Swift can produce a working iOS screen. A Python developer can write a Go service. Somebody who knows infrastructure but not HCL can write a Terraform module. These are real and they matter, particularly for people whose careers were bounded by which language their first job used."}]},{"t":"p","runs":[{"text":"What follows is the list of what still bites, because the failure mode here is specific: the code compiles, the tests pass, and it is wrong in ways that only somebody fluent would see."}]},{"t":"h2","id":"what-still-bites","runs":[{"text":"What still bites"}]},{"t":"p","runs":[{"text":"Error handling conventions.","b":true},{"text":" Go returns errors and you check every one; ignoring one with "},{"text":"_","code":true},{"text":" is a code smell that a Python developer will not notice. Rust’s "},{"text":"Result","code":true},{"text":" demands handling and "},{"text":".unwrap()","code":true},{"text":" is a decision to crash. Java distinguishes checked and unchecked exceptions and the convention around each is cultural. Generated code will produce syntactically valid error handling that is wrong for the language’s norms, and error handling is where reliability lives."}]},{"t":"p","runs":[{"text":"Memory and lifetime.","b":true},{"text":" In C and C++, who owns this pointer and when is it freed. In Rust, why the borrow checker is complaining, which is usually because your design is wrong rather than because the compiler is. In JavaScript, why a closure is keeping a large object alive. These are the categories where a plausible answer that compiles can still leak or crash under load."}]},{"t":"p","runs":[{"text":"Concurrency models are not interchangeable.","b":true},{"text":" Go’s goroutines and channels, Java’s threads and executors, JavaScript’s single-threaded event loop, Python’s GIL, Erlang’s processes. Code translated in spirit from one to another is a category error, and it will look fine."}]},{"t":"p","runs":[{"text":"The standard library you do not know.","b":true},{"text":" You will accept a hand-written implementation of something the language already provides, because you do not know it exists. This is the most common form of the problem and the least harmful."}]},{"t":"p","runs":[{"text":"Build, packaging and tooling.","b":true},{"text":" Gradle, Cargo, Go modules, Maven, CMake, npm workspaces. This is where the days actually go, it is poorly represented in generated answers because it is so project-specific, and it is the part nobody warns you about."}]},{"t":"p","runs":[{"text":"Idioms that signal competence.","b":true},{"text":" Whether you use a "},{"text":"context.Context","code":true},{"text":" as the first parameter, whether you return early, whether you use a builder. Getting these wrong is not a correctness problem: it is a signal to every future reader that nobody here knew the language, which affects how much they trust the rest."}]},{"t":"h2","id":"a-checklist-for-shipping-in-a-language-you-do-not-know","runs":[{"text":"A checklist for shipping in a language you do not know"}]},{"t":"ol","start":1,"items":[[{"text":"Read the official tour or getting-started guide.","b":true},{"text":" Two hours. Go’s Tour, the Rust Book’s first four chapters, the Swift language guide. Not to learn the language: to learn what its people care about, which is what generated code will not tell you."}],[{"text":"Set up the linter and formatter first, on their strictest settings.","b":true},{"text":" "},{"text":"golangci-lint","code":true},{"text":", "},{"text":"clippy","code":true},{"text":", "},{"text":"ruff","code":true},{"text":", "},{"text":"eslint","code":true},{"text":" with a recommended config, "},{"text":"ktlint","code":true},{"text":". These encode the community’s accumulated knowledge of what goes wrong, and they are free. In an unfamiliar language they are worth more than in a familiar one, precisely because you cannot yet supply that judgement."}],[{"text":"Find one high-quality open-source project in the language and read it.","b":true},{"text":" Not all of it. One directory. You are calibrating what good looks like."}],[{"text":"Get a fluent human to review the first thing you ship.","b":true},{"text":" One hour of somebody who knows the language will find things no amount of reading will. If you do not have a colleague, open-source communities and forums will often do it for a small, well-posed change."}],[{"text":"Do not choose the architecture.","b":true},{"text":" Follow the framework’s documented structure exactly, even where it seems clumsy. You do not yet have the standing to deviate, and deviations in an unfamiliar language compound."}],[{"text":"Write down what you do not understand.","b":true},{"text":" A running list. Work through it over weeks. This is the difference between shipping in a language and learning one, and both are legitimate, but be clear with yourself about which you are doing."}]]},{"t":"h2","id":"the-honest-framing","runs":[{"text":"The honest framing"}]},{"t":"p","runs":[{"text":"You can now ship working software in a language you do not know. You cannot yet maintain a system in it, debug it under pressure, judge whether a design fits it, or review someone else’s code in it."}]},{"t":"p","runs":[{"text":"That is a useful capability with a clear boundary, and the mistake is not using it: the mistake is not noticing where the boundary is. Being one week into a language and shipping to production is fine for a small internal tool and reckless for a payment path, and the difference is entirely about what happens when it goes wrong at a time when you are the only person available."}]}],"check":{"question":[{"text":"A Python developer ships a Go service. It passes review by other Python developers, compiles, and works. Six weeks later it fails under load in a way nobody can diagnose. Which of the listed gaps most likely explains this?"}],"options":[{"text":[{"text":"Unfamiliarity with the standard library, so hand-written implementations of built-in functions carry subtle bugs."}],"correct":false,"tempting":[{"text":"The most frequent gap, but reimplemented helpers usually fail visibly and early rather than only under load."}]},{"text":[{"text":"Missing idioms such as "},{"text":"context.Context","code":true},{"text":" threading, which made the code unrecognisable to Go developers asked to help."}],"correct":false,"tempting":[{"text":"Idiom failures damage reviewability and trust, but they do not by themselves produce a load-dependent failure."}]},{"text":[{"text":"A goroutine and channel design translated in spirit from Python’s threading model."}],"correct":true,"tempting":null},{"text":[{"text":"Build and packaging differences, since Go’s module system and dependency resolution differ substantially from pip’s."}],"correct":false,"tempting":[{"text":"Tooling costs days of setup time but produces build failures rather than runtime behaviour that only appears at scale."}]}],"order":[1,2,3,0],"why":null},"prev":{"slug":"refactoring-and-codemods","title":"The task it is best at","minutes":9},"next":{"slug":"when-not-to-reach-for-it","title":"When to put the tool down","minutes":8}}}