[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"global":3,"blogpost-why-val-is-better-than-var":26},{"title":4,"description":5,"home_welcome_badge":6,"home_feature_cards":7,"home_newsletter":20},"Result Crafter Blog","In the era of AI, anyone can generate code overnight. But code that lasts years — you can debug, extend, and maintain — still requires discipline and care.","Welcome to the craft",[8,12,16],{"icon":9,"title":10,"description":11},"code","AI Tools & Workflows","Navigating the new landscape of AI assistance without losing your engineering fundamentals.",{"icon":13,"title":14,"description":15},"bug","Code Smells","Identifying subtle anti-patterns before they become untamable technical debt in your codebase.",{"icon":17,"title":18,"description":19},"cpu","Software Craftsmanship","Techniques for writing clean, testable, and maintainable systems that outlast the current hype cycle.",[21],{"headline":22,"description":23,"placeholder":24,"buttonLabel":25},"Don't miss an essay","Get occasional thoughts on software design, code quality, and building resilient systems delivered straight to your inbox. No spam, ever.","hello@example.com","Subscribe",{"post":27,"directusUrl":179},{"id":28,"title":29,"slug":30,"excerpt":31,"date_created":32,"category":33,"featured_image":34,"content":34,"blocks":35},"6aec18b2-559c-4113-9a3b-645e9ea3fc6d","Why val is better than var","why-val-is-better-than-var","Why immutability matters in Kotlin and how using val over var leads to safer, more predictable code.","2026-04-29T09:44:06.800Z","Kotlin",null,[36,44,53,62,68,74,79,87,93,99,106,113,118,122,126,131,136,139,144,148,152,156,159,163,166,171,175],{"id":37,"sort":38,"collection":39,"item":40},"7cdccc9a-7bd1-41be-a709-df4f0a4edbc1",1,"block_prose",{"id":41,"content":42,"width":43},"3e6703c5-c320-43b9-b135-76dce5c80fa9","\u003Cp>When I first encountered \u003Ccode>val\u003C\u002Fcode> and \u003Ccode>var\u003C\u002Fcode> in Kotlin, they seemed trivial. Three letters each. Just two keywords for declaring variables, right?\u003C\u002Fp>\n\u003Cp>Here's the thing though: behind those three letters lies one of the most powerful ideas in software engineering. \u003Cstrong>Immutability\u003C\u002Fstrong> — the principle that once a value is set, it stays set.\u003C\u002Fp>\n\u003Cp>Think of it this way: \u003Ccode>val\u003C\u002Fcode> is a \u003Cem>locked safe\u003C\u002Fem>. You put something inside, close the door, and that's it. Nobody can change what's in there. \u003Ccode>var\u003C\u002Fcode> is an \u003Cem>open mailbox\u003C\u002Fem> — anyone walking by can swap the contents.\u003C\u002Fp>","full",{"id":45,"sort":46,"collection":47,"item":48},"512e92c4-8551-4c6c-a638-cccd9323f4da",2,"block_code",{"id":49,"code":50,"language":51,"filename":52,"highlight_lines":52,"width":43},11,"\u002F\u002F ❌ Before: var — anyone can reassign\nvar name = \"Pavel\"\nname = \"Alex\"    \u002F\u002F Sure, why not?\nname = \"Sandra\"  \u002F\u002F Changed again...\nname = null       \u002F\u002F Wait, now it's null?!\n\n\u002F\u002F ✅ After: val — set once, done\nval name = \"Pavel\"\nname = \"Alex\"  \u002F\u002F Compilation error! The compiler stops you.","kotlin","",{"id":54,"sort":55,"collection":56,"item":57},"232678b3-caee-4024-ad74-8a6331c87654",3,"block_diagram",{"id":58,"source_type":59,"mermaid_code":60,"image":34,"caption":61,"width":43},6,"mermaid","flowchart TD\n    subgraph val_path [val — Predictable Path]\n        direction TB\n        V1[\"Set value\\nval config = loadConfig()\"] --> V2[\"Read value\\nconfig.database\"]\n        V2 --> V3[\"Done.\\nAlways the same.\"]\n    end\n\n    subgraph var_path [var — Uncertain Path]\n        direction TB\n        M1[\"Set value\\nvar config = loadConfig()\"] --> M2[\"Maybe changed?\\nconfig = ???\"]\n        M2 --> M3[\"Changed again?\\nconfig = ???\"]\n        M3 --> M4[\"What is it now?!\\n😱\"]\n    end\n\n    style val_path fill:#e8f5e9,stroke:#2e7d32,color:#1a1a1a\n    style var_path fill:#ffebee,stroke:#c62828,color:#1a1a1a\n    style V1 fill:#c8e6c9,stroke:#2e7d32,color:#1a1a1a\n    style V2 fill:#c8e6c9,stroke:#2e7d32,color:#1a1a1a\n    style V3 fill:#a5d6a7,stroke:#2e7d32,color:#1a1a1a\n    style M1 fill:#ffcdd2,stroke:#c62828,color:#1a1a1a\n    style M2 fill:#ffcdd2,stroke:#c62828,color:#1a1a1a\n    style M3 fill:#ef9a9a,stroke:#c62828,color:#1a1a1a\n    style M4 fill:#ef9a9a,stroke:#c62828,color:#1a1a1a","val gives you a predictable path. var opens the door to uncertainty.",{"id":63,"sort":64,"collection":39,"item":65},"a5bac07a-1f4b-4e18-96cd-a334871f2f14",4,{"id":66,"content":67,"width":43},"f518e6a4-1d59-4a6f-b711-edb231cf6d21","\u003Cp>When you see \u003Ccode>val\u003C\u002Fcode> in code, you know something powerful: \u003Cstrong>this value will not change\u003C\u002Fstrong>. No need to scroll down 50 lines to check if someone reassigned it. No need to trace through three functions to see if it got mutated. The value is what it says it is.\u003C\u002Fp>\u003Cp>With \u003Ccode>var\u003C\u002Fcode>, every time you read the variable, you have to wonder: \u003Cem>\"Is this still the same value I saw 10 lines ago?\"\u003C\u002Fem> That cognitive load adds up. Fast.\u003C\u002Fp>\u003Cp>This is \u003Cstrong>predictability\u003C\u002Fstrong> — and it's not just a nice-to-have. It's the difference between code you can reason about in your head and code that requires a debugger to understand.\u003C\u002Fp>",{"id":69,"sort":70,"collection":39,"item":71},"fdbeb46e-f8d3-4542-8cd2-1d25f1ccb711",5,{"id":72,"content":73,"width":43},"c73bf879-a0b8-4cdd-a029-9cf0e5c77fc4","\u003Cp>Here's where \u003Ccode>val\u003C\u002Fcode> goes from \"nice convention\" to \"real superpower\": \u003Cstrong>thread safety\u003C\u002Fstrong>.\u003C\u002Fp>\u003Cp>Imagine two threads running at the same time, both reading a variable. If it's a \u003Ccode>val\u003C\u002Fcode>, both threads see the same value. Always. No locks needed, no synchronization, no race conditions. The data is immutable, so there's nothing to fight over.\u003C\u002Fp>\u003Cp>But if it's a \u003Ccode>var\u003C\u002Fcode>? Thread A reads the value. Thread B changes it. Thread A uses the \u003Cem>old\u003C\u002Fem> value. Boom — a bug that only appears under load, at 3 AM, in production. These are the worst bugs to find.\u003C\u002Fp>\u003Cp>The diagram below shows exactly why.\u003C\u002Fp>",{"id":75,"sort":58,"collection":56,"item":76},"d8fc66aa-58e7-4cd9-8e15-4c06a01f4516",{"id":70,"source_type":59,"mermaid_code":77,"image":34,"caption":78,"width":43},"sequenceDiagram\n    participant TA as Thread A\n    participant V as val config\n    participant TB as Thread B\n\n    Note over V: val config = loadConfig()\n    TA->>V: read config.database\n    Note right of TA: ✅ Gets the value\n    TB->>V: read config.database\n    Note right of TB: ✅ Same value, no conflict\n    Note over V: No locks needed!\n\n    participant TA2 as Thread A\n    participant M as var state\n    participant TB2 as Thread B\n\n    Note over M: var state = initialState\n    TA2->>M: read state\n    TB2->>M: write state = updatedState\n    TA2->>TA2: Uses OLD state!\n    Note over M: ⚠️ Race condition!","Thread safety: val is safe by design, var requires synchronization",{"id":80,"sort":81,"collection":82,"item":83},"af18c05a-d3f3-47e7-93b0-3e2eee2cf20d",7,"block_callout",{"id":84,"type":85,"icon":34,"content":86,"width":43},14,"tip","\u003Cp>\u003Cstrong>Real talk:\u003C\u002Fstrong> In my Kotlin codebase, about \u003Cstrong>95% of variables are val\u003C\u002Fstrong>. When I see \u003Ccode>var\u003C\u002Fcode>, I treat it as a red flag — something that needs justification. Why does this need to change? Could I restructure the code to avoid it?\u003C\u002Fp>\u003Cp>This single habit — defaulting to \u003Ccode>val\u003C\u002Fcode> — eliminates entire categories of bugs.\u003C\u002Fp>",{"id":88,"sort":89,"collection":39,"item":90},"9ed4b593-3fc3-44e1-8dc6-d05ca5c6be6d",8,{"id":91,"content":92,"width":43},"ebe5d3b4-be33-4323-aad0-a6977ef4d93c","\u003Cp>So when \u003Cem>should\u003C\u002Fem> you use \u003Ccode>var\u003C\u002Fcode>? Honestly, not often. But there are legitimate cases:\u003C\u002Fp>\u003Cul>\u003Cli>Loop counters (\u003Ccode>for (i in 0..n)\u003C\u002Fcode> — though Kotlin's \u003Ccode>for\u003C\u002Fcode> often avoids this)\u003C\u002Fli>\u003Cli>State machines that \u003Cem>must\u003C\u002Fem> mutate\u003C\u002Fli>\u003Cli>Performance-critical code where object allocation matters\u003C\u002Fli>\u003Cli>Building something in stages where you can't use a constructor\u003C\u002Fli>\u003C\u002Ful>\u003Cp>The key is intentionality. Don't use \u003Ccode>var\u003C\u002Fcode> out of habit. Use it because you \u003Cem>understand why this specific value needs to change\u003C\u002Fem>. And when you do, add a comment explaining why.\u003C\u002Fp>",{"id":94,"sort":95,"collection":56,"item":96},"7bf6b9fc-66ac-4daf-8f82-9867ee92fec0",9,{"id":64,"source_type":59,"mermaid_code":97,"image":34,"caption":98,"width":43},"flowchart TD\n    Start[\"Need a variable?\"] --> Q1{\"Will the value\\nchange?\"}\n    Q1 -->|\"No\"| VAL1[\"✅ Use val\"]\n    Q1 -->|\"Yes\"| Q2{\"Is mutation\\ntruly necessary?\"}\n    Q2 -->|\"No\"| VAL2[\"✅ Use val\\nRestructure your code\"]\n    Q2 -->|\"Yes\"| Q3{\"Can you explain\\nwhy it must change?\"}\n    Q3 -->|\"No\"| VAL3[\"✅ Use val\\nYou're not sure yet\"]\n    Q3 -->|\"Yes\"| VAR[\"⚠️ Use var\\nAdd a comment explaining why\"]\n\n    style VAL1 fill:#c8e6c9,stroke:#2e7d32,color:#1a1a1a\n    style VAL2 fill:#c8e6c9,stroke:#2e7d32,color:#1a1a1a\n    style VAL3 fill:#c8e6c9,stroke:#2e7d32,color:#1a1a1a\n    style VAR fill:#fff9c4,stroke:#f57f17,color:#1a1a1a\n    style Start fill:#e3f2fd,stroke:#1565c0,color:#1a1a1a\n    style Q1 fill:#e3f2fd,stroke:#1565c0,color:#1a1a1a\n    style Q2 fill:#e3f2fd,stroke:#1565c0,color:#1a1a1a\n    style Q3 fill:#e3f2fd,stroke:#1565c0,color:#1a1a1a","The var radar: your decision tree for choosing val or var",{"id":100,"sort":101,"collection":102,"item":103},"e1a28202-1847-439c-ba2e-86fdd270c25e",10,"block_quote",{"id":70,"text":104,"author":105,"source":52,"width":43},"Start with val. Let the compiler tell you when you actually need var.","Rule of thumb",{"id":107,"sort":108,"collection":102,"item":109},"063b071f-7a17-460f-b91b-06cd03bb6eeb",999,{"id":38,"text":110,"author":111,"source":112,"width":43},"\u003Cp>Mutable state is the new spaghetti code. Each mutable variable is like a \u003Ccode>GOTO\u003C\u002Fcode> — it makes reasoning about the program harder because the value can change at any time. Immutable values, on the other hand, are like mathematical definitions — they don't change, and you can trust them.\u003C\u002Fp>","Robert C. Martin","Clean Code",{"id":114,"sort":108,"collection":39,"item":115},"e443e13a-b889-45fe-af41-75ebe7a5dffa",{"id":116,"content":117,"width":43},"994b1537-da5e-4732-97db-528fba8cfed8","\u003Cp>In Kotlin, \u003Ccode>val\u003C\u002Fcode> means read-only. \u003Ccode>var\u003C\u002Fcode> means mutable. That's the syntax. But the real question is: \u003Cstrong>which one should you use, and why does it matter?\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>The short answer: default to \u003Ccode>val\u003C\u002Fcode>. It makes your code safer, easier to read, and harder to break. Let me show you why.\u003C\u002Fp>",{"id":119,"sort":108,"collection":47,"item":120},"88dcc0c1-8c13-4c65-b89a-7c91437695f9",{"id":58,"code":121,"language":51,"filename":34,"highlight_lines":34,"width":43},"\u002F\u002F val — can't be reassigned (read-only)\nval name = \"Alice\"\n\u002F\u002F name = \"Bob\"  ← Compiler error!\n\n\u002F\u002F var — can be reassigned (mutable)\nvar score = 0\nscore = 100  \u002F\u002F ✓ Works fine\n\n\u002F\u002F val with a conditional expression\nval status = if (score > 50) \"pass\" else \"fail\"\n\u002F\u002F status is still read-only, but assigned dynamically",{"id":123,"sort":108,"collection":39,"item":124},"945c8d6d-155b-4589-a526-44c186141c5a",{"id":125,"content":52,"width":43},"e5fa196b-87b7-4935-be22-f86c3f07eaec",{"id":127,"sort":108,"collection":56,"item":128},"cc8ecfc2-4c3f-4a8c-944b-da4bcd81eee5",{"id":38,"source_type":59,"mermaid_code":129,"image":34,"caption":130,"width":43},"flowchart TD\n    A[You need a variable] --> B{Will its value change\n    during its lifetime?}\n    B -->|No| C[Use val ✓]\n    B -->|Yes| D{Is the change intentional\n    and necessary?}\n    D -->|No| C\n    D -->|Yes| E{Can you restructure\n    as an expression?}\n    E -->|Yes| F[Use val with\n    when\u002Fif expression ✓]\n    E -->|No| G[Use var —\n    but document why]\n    \n    style C fill:#059669,color:#fff\n    style F fill:#059669,color:#fff\n    style G fill:#d97706,color:#fff","Decision flowchart: Should this variable be val or var?",{"id":132,"sort":108,"collection":82,"item":133},"f1562783-7dd2-4cd7-b579-e5183b4de864",{"id":101,"type":134,"icon":34,"content":135,"width":43},"info","\u003Cp>\u003Cstrong>Start with val.\u003C\u002Fstrong> You can always change it to \u003Ccode>var\u003C\u002Fcode> later when you find a real need. Going the other way is much harder.\u003C\u002Fp>",{"id":137,"sort":108,"collection":102,"item":138},"ec453431-d42c-4d62-8dc6-5a2dbc73e993",{"id":38,"text":110,"author":111,"source":112,"width":43},{"id":140,"sort":108,"collection":39,"item":141},"c8ad52aa-e0c2-4ff4-bda8-63ba99d5b7d7",{"id":142,"content":143,"width":43},"f5e42e20-87ed-457a-b158-cc221d5fc908","\u003Ch2>When var is actually needed\u003C\u002Fh2>\u003Cp>\u003Ccode>var\u003C\u002Fcode> isn't evil. There are real cases where you need it:\u003C\u002Fp>\u003Cul>\u003Cli>\u003Cstrong>Loop accumulators\u003C\u002Fstrong> — building a total across iterations\u003C\u002Fli>\u003Cli>\u003Cstrong>State machines\u003C\u002Fstrong> — the current state genuinely changes\u003C\u002Fli>\u003Cli>\u003Cstrong>Performance hot paths\u003C\u002Fstrong> — where reallocating is measurably slower\u003C\u002Fli>\u003C\u002Ful>\u003Cp>But here's the trick: most \u003Ccode>var\u003C\u002Fcode> declarations can become \u003Ccode>val\u003C\u002Fcode> by using \u003Ccode>when\u003C\u002Fcode> or \u003Ccode>if\u003C\u002Fcode> expressions. Kotlin makes this natural.\u003C\u002Fp>",{"id":145,"sort":108,"collection":39,"item":146},"be8d6598-5b22-445e-985c-9e29b82ecec9",{"id":147,"content":52,"width":43},"25b02599-bfe7-4829-b5ad-24558cd0437b",{"id":149,"sort":108,"collection":47,"item":150},"6ac3b7c6-247b-4e3d-b889-8431f7165b0f",{"id":38,"code":52,"language":51,"filename":151,"highlight_lines":34,"width":43},"ValVsVar.kt",{"id":153,"sort":108,"collection":39,"item":154},"20165eeb-7ab0-4747-aeb2-62e6c63f5674",{"id":155,"content":52,"width":43},"e072ce16-8369-4d73-a977-d8d99ca2530e",{"id":157,"sort":108,"collection":82,"item":158},"8abe463d-466c-4f58-bdd9-84ff4253e9df",{"id":38,"type":134,"icon":34,"content":52,"width":43},{"id":160,"sort":108,"collection":39,"item":161},"67eb781b-679a-4e5d-add2-75f8eea7e0cd",{"id":162,"content":52,"width":43},"8a70131f-5156-4cb8-88c5-88e41c300101",{"id":164,"sort":108,"collection":56,"item":165},"61f2b76e-27b4-4ebd-97b8-984e765b8559",{"id":38,"source_type":59,"mermaid_code":129,"image":34,"caption":130,"width":43},{"id":167,"sort":108,"collection":47,"item":168},"b8db8f12-2f0b-4adb-98ac-fb92b9e9fbe9",{"id":46,"code":52,"language":51,"filename":169,"highlight_lines":170,"width":43},"ConcurrencyBug.kt","1,8,9,16,17,18,19,20,21",{"id":172,"sort":108,"collection":39,"item":173},"8af6eb27-f8fc-46ca-8360-5edc1fe98dea",{"id":174,"content":52,"width":43},"5e624817-6211-40b8-a369-0b154677d64a",{"id":176,"sort":108,"collection":82,"item":177},"9d3122d3-ab46-4ed9-904d-ccdf35b253e2",{"id":46,"type":178,"icon":34,"content":52,"width":43},"success","https:\u002F\u002Fd1.resultcrafter.com"]