<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Kornilov Tools]]></title><description><![CDATA[Practical tools for freelancers and developers. Local workspaces for scope changes, CSV checks, launches and career evidence. RU/EN.]]></description><link>https://swiftuidev.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 04 Nov 2026 19:10:49 GMT</lastBuildDate><atom:link href="https://swiftuidev.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[You Have a Backup. Can You Restore It?]]></title><description><![CDATA[You saved a backup file before changing computers. When you try to import it, you discover it is old or missing the record you need. You can test the…]]></description><link>https://swiftuidev.hashnode.dev/you-have-a-backup-can-you-restore-it</link><guid isPermaLink="true">https://swiftuidev.hashnode.dev/you-have-a-backup-can-you-restore-it</guid><dc:creator><![CDATA[Daniil Kornilov]]></dc:creator><pubDate>Thu, 29 Oct 2026 16:23:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/697f963120e4834274ec287b/40b45ba6-c020-426e-845c-de24d29de843.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You saved a backup file before changing computers. When you try to import it, you discover it is old or missing the record you need.</p>
<p>You can test the recovery path before depending on it. Start with a small example and use a safe, separate test environment.</p>
<h2>Check one useful record</h2>
<p>Imagine a project planner that exports JSON. Create a test task with a recognizable title, export the file, then import it into an isolated test copy. Find the task and compare its details with the original.</p>
<p>Some applications replace current data during import. Read the warning and keep the test away from your working project. Protect any private data in the backup as carefully as the original.</p>
<p>Record the version you tested, the file used and the result. If important data is missing, investigate and make another copy before relying on it. Repeat the check after significant changes to the export or import process.</p>
<h2>Keep track of the check</h2>
<p>Launch Control Desk helps you organize release checks, owners, results and risks in a local workspace. It records your checks; it does not back up your service or run the restore for you.</p>
<p>RUB 1,190 for a one-time purchase: <a href="https://boosty.to/swiftuidev/posts/68708fd5-1dad-4ed3-ac0c-3356d796a00f?utm_source=hashnode&amp;utm_medium=post&amp;utm_campaign=restore_test_20261029">Get Launch Control Desk</a>.</p>
<p>I'm Daniil Kornilov, author of Kornilov Tools. Follow this blog for practical checks for websites and digital products.</p>
<p>When did you last try restoring a backup?</p>
]]></content:encoded></item><item><title><![CDATA[Your Team Built the Project. What Did You Build?]]></title><description><![CDATA[A team project can make a strong portfolio case when a reader can see your part of the work. Give the project context, then explain your contribution with…]]></description><link>https://swiftuidev.hashnode.dev/your-team-built-the-project-what-did-you-build</link><guid isPermaLink="true">https://swiftuidev.hashnode.dev/your-team-built-the-project-what-did-you-build</guid><dc:creator><![CDATA[Daniil Kornilov]]></dc:creator><pubDate>Thu, 29 Oct 2026 16:22:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/697f963120e4834274ec287b/e595fc35-f870-404f-83d5-97a400051389.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A team project can make a strong portfolio case when a reader can see your part of the work. Give the project context, then explain your contribution with a result someone can inspect.</p>
<h2>Make your part visible</h2>
<p>Imagine a team built an online shop and you worked on the delivery-address form. If that matches your real work, you could write:</p>
<blockquote>
<p>Our team built an online shop. I added an empty-address check and a message that tells the customer what to enter. The screenshot shows the message after an empty submission.</p>
</blockquote>
<p>This example is fictional. Use your own task and describe the actual contribution. A link to an accessible pull request, a screenshot or a demo can help support the specific claim. Credit other contributors where relevant.</p>
<p>If the project is private, share only what you have permission to share. An anonymized explanation or a separate sample can work; label a sample clearly.</p>
<h2>Put the case together</h2>
<p>Portfolio Case Kit is a local editor for your profile and project cases. It can export a portfolio website and GitHub README text. Save a separate JSON copy of your working data.</p>
<p>RUB 790 for a one-time purchase: <a href="https://boosty.to/swiftuidev/posts/473522d8-bdf3-462d-8005-898fcc5a29b2?utm_source=hashnode&amp;utm_medium=post&amp;utm_campaign=team_case_20261029">Get Portfolio Case Kit</a>.</p>
<p>I'm Daniil Kornilov, author of Kornilov Tools. Follow this blog for practical examples for projects and portfolios.</p>
<p>How do you show your part of a team project?</p>
]]></content:encoded></item><item><title><![CDATA[Your Product Code Started With a Zero. Where Did It Go?]]></title><description><![CDATA[A product code in your source system is 001. After opening an export in a spreadsheet and saving it again, you see 1. If the spreadsheet treated the code…]]></description><link>https://swiftuidev.hashnode.dev/your-product-code-started-with-a-zero-where-did-it-go</link><guid isPermaLink="true">https://swiftuidev.hashnode.dev/your-product-code-started-with-a-zero-where-did-it-go</guid><category><![CDATA[csv]]></category><dc:creator><![CDATA[Daniil Kornilov]]></dc:creator><pubDate>Wed, 28 Oct 2026 21:19:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/697f963120e4834274ec287b/c5988182-c739-4fee-9acb-2090684b1c57.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A product code in your source system is <code>001</code>. After opening an export in a spreadsheet and saving it again, you see <code>1</code>.</p>
<p>If the spreadsheet treated the code as a number, it may have removed the leading zeros. That matters when the code is an identifier: <code>001</code> and <code>1</code> can represent different records.</p>
<h2>Check the source before changing anything</h2>
<p>Open a copy of the original CSV in a plain-text editor and find a known code. Compare it with the version you saved from the spreadsheet. If the original says <code>001</code> and the new file says <code>1</code>, the change happened somewhere between those versions.</p>
<p>A CSV can contain <code>001</code> as text, but a spreadsheet may interpret that value as a number when opening it. When importing a file, set the product-code column to <strong>Text</strong> in the import settings. Then inspect a few known IDs before saving a new copy. Quoting a CSV value alone doesn't guarantee that a spreadsheet will preserve its zeros.</p>
<p>If the zeros are already missing, don't add them back by guessing. Check the source system or an unchanged export to learn the real IDs. Different codes may have different lengths.</p>
<h2>Compare two versions using the right key</h2>
<p>CSV Reconcile Desk compares two CSV or TSV files by an ID column. It treats <code>001</code> and <code>1</code> as different strings, so they won't silently match. Select the fields you want to compare and review added, missing and changed records; duplicate and blank IDs are listed separately.</p>
<p>The app runs locally in your browser and can export a CSV or JSON report. It does not restore lost zeros or decide which code is correct; you need the original source for that. XLSX is not supported.</p>
<p>RUB 990 as a one-time purchase: <a href="https://boosty.to/swiftuidev/posts/5e3a3e7d-0796-4152-bdae-5696dcfbd499?utm_source=hashnode&amp;utm_medium=post&amp;utm_campaign=csv_zeros_20261028">Get CSV Reconcile Desk</a>.</p>
<p>I'm Daniil Kornilov, author of Kornilov Tools. Follow this blog for simple ways to check files and avoid confusing data errors.</p>
<p>Have you ever seen an identifier change after a file was opened or exported?</p>
]]></content:encoded></item><item><title><![CDATA[A Job Asks for a Tool You Haven't Used. How Do You Explain Your Experience?]]></title><description><![CDATA[A job description mentions a tool you haven't worked with. You do have experience solving a similar problem with something else. Don't claim you've used…]]></description><link>https://swiftuidev.hashnode.dev/a-job-asks-for-a-tool-you-haven-t-used-how-do-you-explain-your-experience</link><guid isPermaLink="true">https://swiftuidev.hashnode.dev/a-job-asks-for-a-tool-you-haven-t-used-how-do-you-explain-your-experience</guid><category><![CDATA[Career]]></category><dc:creator><![CDATA[Daniil Kornilov]]></dc:creator><pubDate>Wed, 28 Oct 2026 21:18:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/697f963120e4834274ec287b/44dda5f4-0ff4-49af-930d-34e7b78f26a1.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A job description mentions a tool you haven't worked with. You do have experience solving a similar problem with something else.</p>
<p>Don't claim you've used the missing tool. Make the connection clear and describe the gap honestly.</p>
<h2>Use one real example</h2>
<p>Imagine a role asks for Docker, and you've deployed an application without using it. If that's true for you, a useful answer might be:</p>
<blockquote>
<p>I haven't used Docker professionally. I have deployed a small application, configured its environment variables and checked the logs when it failed to start. I understand Docker packages an application and its dependencies into an image. I'm practising that workflow and can explain what I've tried.</p>
</blockquote>
<p>That answer separates actual experience from what you're learning. Only include the final sentence if you really have started practising. If you haven't, say what you plan to learn next instead.</p>
<p>Then prepare to explain the real work: what was failing, what you checked and what changed. If it was a team project, make your own contribution clear.</p>
<h2>Map the requirement before the interview</h2>
<p>Write down three things: the requirement, the closest relevant task you've actually done, and what you still need to learn. Bring a brief example you can discuss, plus any evidence you can share without revealing confidential information.</p>
<p>Some requirements are essential for a role, and a related example may not replace them. The point is to give the interviewer an accurate picture of what you can do today.</p>
<p>Interview Proof Kit is a local workspace for connecting job requirements to your own stories, evidence, gaps and next steps. It includes practice prompts and lets you export your notes. It doesn't verify your claims or predict hiring outcomes. Save a separate JSON backup of your notes.</p>
<p>RUB 990 as a one-time purchase: <a href="https://boosty.to/swiftuidev/posts/da3bb6a5-a83c-4af7-afe7-4bb9751eccfc?utm_source=hashnode&amp;utm_medium=post&amp;utm_campaign=interview_gap_20261028">Get Interview Proof Kit</a>.</p>
<p>I'm Daniil Kornilov, author of Kornilov Tools. Follow this blog for practical examples for interviews and everyday development work.</p>
<p>What related experience would you use to explain a skill you're still learning?</p>
]]></content:encoded></item><item><title><![CDATA[Someone Bought Your File. Can They Get Started?]]></title><description><![CDATA[A customer buys your digital file. Payment succeeds, and a download link appears. Can they open the file and get a first result without asking for help? A…]]></description><link>https://swiftuidev.hashnode.dev/someone-bought-your-file-can-they-get-started</link><guid isPermaLink="true">https://swiftuidev.hashnode.dev/someone-bought-your-file-can-they-get-started</guid><category><![CDATA[Testing]]></category><dc:creator><![CDATA[Daniil Kornilov]]></dc:creator><pubDate>Wed, 28 Oct 2026 01:44:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/697f963120e4834274ec287b/ed940026-351c-4bde-8d53-8ad7d7c959a8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A customer buys your digital file. Payment succeeds, and a download link appears.</p>
<p>Can they open the file and get a first result without asking for help?</p>
<p>A successful upload tells you the package reached the platform. It doesn't show what the buyer receives or whether your instructions match the package.</p>
<h2>Try the buyer's route</h2>
<p>Imagine you sell a downloadable template. Before release, get the file through the same delivery route a buyer uses, using a test purchase if the platform supports one. If you can't use that route without paying, check the available delivery preview and record what you couldn't test.</p>
<p>On a device or account without your working files, follow the steps as a customer would:</p>
<ol>
<li>Download the delivered file and check that it opens.</li>
<li>If it's an archive, extract it and find the starting file mentioned in the instructions.</li>
<li>Follow the instructions to create a small example or open the included sample.</li>
<li>Check that the result appears and that the instructions explain how to save it, if saving is part of the product.</li>
</ol>
<p>If you get stuck, record the step and fix the file or instructions. Then test again with a fresh copy.</p>
<p>Make the first instruction concrete: “Extract the ZIP, then open Start Here” is easier to follow than “Use the included materials.” Only name a file that's actually in the package, and tell buyers about required software before purchase.</p>
<h2>Keep launch checks together</h2>
<p>Launch Control Desk helps organize checks before release. You can assign an owner, record the outcome and see what still needs fixing. The package includes a local RU/EN app, examples and instructions. You perform the checks yourself; the app records the work.</p>
<p>RUB 1,190 for a one-time purchase: <a href="https://boosty.to/swiftuidev/posts/68708fd5-1dad-4ed3-ac0c-3356d796a00f?utm_source=hashnode&amp;utm_medium=post&amp;utm_campaign=first_step_20261028">Get Launch Control Desk</a>.</p>
<p>I'm Daniil Kornilov, author of Kornilov Tools. Follow this blog for simple ways to check a product before selling it.</p>
<p>What's the first action a customer should take after downloading your product?</p>
]]></content:encoded></item><item><title><![CDATA[No Metrics for Your Portfolio? Show What You Can Prove]]></title><description><![CDATA[You built a small expense tracker for your portfolio. It has no customers yet, and you haven't measured whether it saves anyone time. That doesn't mean you…]]></description><link>https://swiftuidev.hashnode.dev/no-metrics-for-your-portfolio-show-what-you-can-prove</link><guid isPermaLink="true">https://swiftuidev.hashnode.dev/no-metrics-for-your-portfolio-show-what-you-can-prove</guid><category><![CDATA[Career]]></category><dc:creator><![CDATA[Daniil Kornilov]]></dc:creator><pubDate>Wed, 28 Oct 2026 01:43:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/697f963120e4834274ec287b/7433ab4b-7465-43a7-a30b-ea7487236600.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You built a small expense tracker for your portfolio. It has no customers yet, and you haven't measured whether it saves anyone time.</p>
<p>That doesn't mean you have nothing useful to show. Explain what the project does and offer evidence a reader can actually check.</p>
<h2>Turn a feature into an example</h2>
<p>Imagine the tracker lets someone enter an amount, choose a category and see a monthly total. Here's a fictional way to describe it:</p>
<blockquote>
<p>This personal project groups expenses by category. To check the total, add two expenses in the same month, then remove one and confirm the total changes. The demo and its test steps are linked below.</p>
</blockquote>
<p>A reader can follow the example and inspect the behavior. That's more useful than claiming the app “improved productivity by 40%” when no such result was measured.</p>
<p>For your own project, use its real behavior and actual links. A screenshot, a test or a working demo can support a specific claim. It can't prove that people used the app or saved money.</p>
<h2>Explain the limits, too</h2>
<p>If it was a team project, say which part you built. Mention whether the example uses sample data and what the app doesn't yet handle. For instance, if entries stay in one browser, explain that before someone expects them to appear on another device.</p>
<p>A small, honest result gives readers a way to understand your work. Add measured outcomes later if you collect them.</p>
<h2>Put the case together</h2>
<p>Portfolio Case Kit is a local editor for presenting your projects. Add a profile and project details, then export a portfolio website or text for a GitHub README. The package includes instructions and fictional examples. Save a separate JSON copy of your working data.</p>
<p>RUB 790 for a one-time purchase: <a href="https://boosty.to/swiftuidev/posts/473522d8-bdf3-462d-8005-898fcc5a29b2?utm_source=hashnode&amp;utm_medium=post&amp;utm_campaign=portfolio_proof_20261028">Get Portfolio Case Kit</a>.</p>
<p>I'm Daniil Kornilov, author of Kornilov Tools. Follow this blog for practical examples for projects and portfolios.</p>
<p>What's one thing someone can try or inspect in your latest project?</p>
]]></content:encoded></item><item><title><![CDATA[One Product ID, Two Rows: Which One Should You Trust?]]></title><description><![CDATA[You're comparing stock in two files. In the new file, item A-17 appears twice: one row says there are 2 left, the other says 5. Which row is correct? The…]]></description><link>https://swiftuidev.hashnode.dev/one-product-id-two-rows-which-one-should-you-trust</link><guid isPermaLink="true">https://swiftuidev.hashnode.dev/one-product-id-two-rows-which-one-should-you-trust</guid><category><![CDATA[data analysis]]></category><dc:creator><![CDATA[Daniil Kornilov]]></dc:creator><pubDate>Tue, 27 Oct 2026 10:33:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/697f963120e4834274ec287b/f047fdaa-937e-41eb-b792-31b15e29bead.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You're comparing stock in two files. In the new file, item A-17 appears twice: one row says there are 2 left, the other says 5.</p>
<p>Which row is correct? The two rows alone don't tell you.</p>
<p>Perhaps the item was duplicated by mistake. Or perhaps the rows refer to different warehouses, but the file doesn't include the warehouse name. Taking the first row automatically can produce a tidy-looking comparison with the wrong answer.</p>
<h2>Separate the uncertain rows</h2>
<p>Put repeated IDs aside for review. Check the original source and look for a field that distinguishes the records, such as warehouse or product variant.</p>
<p>Then decide what makes a record unique for your task. If one product can appear once per warehouse, the product ID alone isn't enough. You may need a key that includes both product and warehouse.</p>
<p>For example, A-17 / north and A-17 / south can represent two different records. Only use those labels if the source actually provides that distinction. Don't invent missing information to make the duplicate disappear.</p>
<p>You can continue comparing unambiguous rows while keeping uncertain ones visible in a separate review list. Fix or clarify the source before treating those rows as reliable.</p>
<h2>Make the uncertain rows visible</h2>
<p>CSV Reconcile Desk compares two CSV or TSV files by ID and shows new, missing and changed records. Duplicate and blank IDs are separated for review, and you can save the result as CSV or JSON.</p>
<p>The app runs locally in your browser. Extract the archive and open index.html. It uses one ID column, so if you need a combined key, prepare it in a separate column first. XLSX isn't supported.</p>
<p>RUB 990 for a one-time purchase: <a href="https://boosty.to/swiftuidev/posts/5e3a3e7d-0796-4152-bdae-5696dcfbd499?utm_source=hashnode&amp;utm_medium=post&amp;utm_campaign=duplicate_id_20261027">Get CSV Reconcile Desk</a>.</p>
<p>I'm Daniil Kornilov, author of Kornilov Tools. Follow this blog for practical examples and tools for everyday work.</p>
<p>When you find duplicate IDs, what do you check first?</p>
]]></content:encoded></item><item><title><![CDATA[You Changed the Price. What Does the Customer Actually See?]]></title><description><![CDATA[You update the price on a product page and refresh. Everything looks right. But the customer still has to add the item to the cart and reach checkout. A…]]></description><link>https://swiftuidev.hashnode.dev/you-changed-the-price-what-does-the-customer-actually-see</link><guid isPermaLink="true">https://swiftuidev.hashnode.dev/you-changed-the-price-what-does-the-customer-actually-see</guid><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Daniil Kornilov]]></dc:creator><pubDate>Tue, 27 Oct 2026 10:32:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/697f963120e4834274ec287b/0d0452d1-2c5d-4d3d-a41c-e9b0c0b4618e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You update the price on a product page and refresh. Everything looks right.</p>
<p>But the customer still has to add the item to the cart and reach checkout. A correct product page does not tell you what the next steps will show.</p>
<p>Imagine the page says $40 while the cart still shows the old price. The customer notices the difference just as they are ready to order.</p>
<h2>Follow the price through the journey</h2>
<p>Open the public site on your phone, without signing in as an administrator. Find the product, add it to the cart and go to checkout.</p>
<p>Check the item price at each step. Then look at the final total: shipping, taxes or other fees may make it different from the item price. The key is whether the customer can see what makes up the total.</p>
<p>You don't need to pay to check the displayed price. If you must place a test order to check a later step, clearly mark it as a test and make sure nobody starts fulfilling it.</p>
<h2>Keep the result with your launch plan</h2>
<p>A note saying “price checked” can be easy to misunderstand. Write down which product you checked, what you saw in the cart and checkout, and anything that still needs fixing.</p>
<p>Launch Control Desk helps keep launch tasks, owners and results together in one local workspace. You do the checks yourself; the tool records the work. The package includes a RU/EN app, examples and instructions. Extract the archive and open index.html.</p>
<p>It costs RUB 1,190 as a one-time purchase. <a href="https://boosty.to/swiftuidev/posts/68708fd5-1dad-4ed3-ac0c-3356d796a00f?utm_source=hashnode&amp;utm_medium=post&amp;utm_campaign=price_check_20261027">Get Launch Control Desk</a>.</p>
<p>I'm Daniil Kornilov, author of Kornilov Tools. Follow this blog for practical website checks and tools for everyday work.</p>
<p>When did you last go through checkout on your own site?</p>
]]></content:encoded></item><item><title><![CDATA[A Beautiful Contact Form Is Useless If Nobody Gets the Message]]></title><description><![CDATA[Your contact form looks finished. The button responds, and a green message thanks the visitor. Before calling the job done, follow one inquiry to the…]]></description><link>https://swiftuidev.hashnode.dev/a-beautiful-contact-form-is-useless-if-nobody-gets-the-message</link><guid isPermaLink="true">https://swiftuidev.hashnode.dev/a-beautiful-contact-form-is-useless-if-nobody-gets-the-message</guid><category><![CDATA[webdev]]></category><dc:creator><![CDATA[Daniil Kornilov]]></dc:creator><pubDate>Mon, 26 Oct 2026 19:06:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/697f963120e4834274ec287b/e7a95cb4-bb07-4c0e-8b37-36e950055e6a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Your contact form looks finished. The button responds, and a green message thanks the visitor.</p>
<p>Before calling the job done, follow one inquiry to the person who will answer it.</p>
<h2>Test the whole conversation</h2>
<p>For a website that delivers inquiries by email, try this:</p>
<ol>
<li>Open the public site on a phone, signed out of the admin account.</li>
<li>Send a clearly marked test inquiry using an email address you control.</li>
<li>Find that exact message in the intended inbox.</li>
<li>Check that its text and reply address survived.</li>
<li>Reply and check that your test address receives the answer.</li>
</ol>
<p>This gives you something specific to record: the inquiry arrived, and the recipient could answer it.</p>
<p>If the message is missing, check the form service's record, the destination address and the spam folder. Find where the route stopped before changing things at random.</p>
<p>One successful message does not guarantee future delivery. Repeat the check when you change the form or its destination, and keep a way to notice failures after launch.</p>
<h2>Keep the result beside the task</h2>
<p>“Contact form checked” is easy to misunderstand later.</p>
<p>“Test inquiry received in the support inbox; reply received at the test address” tells the next person what actually happened. Add the date, site version and person who checked it to your private release notes.</p>
<p>I'm Daniil Kornilov, the author of Kornilov Tools. Launch Control Desk helps organize checks, owners, results and remaining launch risks in one local RU/EN workspace. You perform the checks yourself; the app records the outcomes.</p>
<p>The package includes the app, examples and instructions. Extract the archive, open index.html and export separate JSON backups of your plan.</p>
<p>RUB 1,190 for a one-time purchase, with no mandatory subscription inside the app. <a href="https://boosty.to/swiftuidev/posts/68708fd5-1dad-4ed3-ac0c-3356d796a00f?utm_source=hashnode&amp;utm_medium=post&amp;utm_campaign=contact_20261026">Buy Launch Control Desk</a>.</p>
<p>Follow this blog for simple website checks and practical tools for small projects.</p>
<p>When did you last send a message through your own contact form?</p>
]]></content:encoded></item><item><title><![CDATA[You Wanted a Bigger Button. Why Did AI Redesign the Page?]]></title><description><![CDATA[Imagine a contact button that is hard to find on a phone. You ask an assistant: “Make this page better.” It changes the colors, rewrites the headline and…]]></description><link>https://swiftuidev.hashnode.dev/you-wanted-a-bigger-button-why-did-ai-redesign-the-page</link><guid isPermaLink="true">https://swiftuidev.hashnode.dev/you-wanted-a-bigger-button-why-did-ai-redesign-the-page</guid><category><![CDATA[Productivity]]></category><dc:creator><![CDATA[Daniil Kornilov]]></dc:creator><pubDate>Sun, 25 Oct 2026 19:30:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/697f963120e4834274ec287b/aedbb0c7-473a-4aae-8c29-dc0fa0cef0d2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine a contact button that is hard to find on a phone.</p>
<p>You ask an assistant: “Make this page better.”</p>
<p>It changes the colors, rewrites the headline and moves several sections. Maybe those changes look fine. They still make a small fix harder to review.</p>
<p>The request left the size of the job open.</p>
<h2>Name the problem and the boundary</h2>
<p>Try this instead:</p>
<blockquote>
<p>On a 360px-wide screen, make the contact button easy to find in the first section. Change only its size and spacing. Keep the text, colors and desktop layout. Show me what changed before doing anything else.</p>
</blockquote>
<p>Now you have something concrete to review. Open the page at that width. Find the button. Check that it does not cause sideways scrolling. Compare the desktop layout.</p>
<p>This is a clearer assignment, not a guarantee about how a model will behave. Inspect what it actually changed.</p>
<p>If the allowed change cannot solve the problem, ask for an explanation before making the job bigger.</p>
<h2>Write the finish line first</h2>
<p>Before sending a request, finish this sentence:</p>
<blockquote>
<p>I will know this is done when…</p>
</blockquote>
<p>For the button: “I can find it in the first section on a phone, and the rest of the page still looks the same.”</p>
<p>For an article: “The introduction is shorter, the jargon is gone and one useful example remains.”</p>
<p>For a spreadsheet: “The totals match the supplied example, and the original data is preserved.”</p>
<p>The point is to choose an outcome you can check. “Better” alone does not tell you when to stop.</p>
<h2>Keep client changes visible too</h2>
<p>When a client requests another change, keep the original agreement beside the new request. That makes the difference easier to discuss before work starts.</p>
<p>I'm Daniil Kornilov, the author of Kornilov Tools. Scope Change Desk helps record project scope, revision rounds and requests for additional work. It includes a local RU/EN app, an Excel workbook, examples and instructions. Extract the archive and open index.html; export separate JSON backups of your project.</p>
<p>RUB 790 for a one-time purchase. <a href="https://boosty.to/swiftuidev/posts/7175948d-3f36-46e8-8553-8109a21d79cb?utm_source=hashnode&amp;utm_medium=post&amp;utm_campaign=clear_20261025">Buy Scope Change Desk</a>.</p>
<p>Follow this blog for plain-language breakdowns of practical work problems and useful tools.</p>
<p>What vague request would you like to stop receiving?</p>
]]></content:encoded></item><item><title><![CDATA[Three Broken Shipping Functions. One Green Test.]]></title><description><![CDATA[Three deliberately broken shipping functions. The same test passes every one. That result should make a green test badge feel less reassuring. Here is a…]]></description><link>https://swiftuidev.hashnode.dev/three-broken-shipping-functions-one-green-test</link><guid isPermaLink="true">https://swiftuidev.hashnode.dev/three-broken-shipping-functions-one-green-test</guid><category><![CDATA[Testing]]></category><dc:creator><![CDATA[Daniil Kornilov]]></dc:creator><pubDate>Sat, 24 Oct 2026 12:30:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/697f963120e4834274ec287b/c6980e35-03cb-4161-bece-7fcaf9d793c7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Three deliberately broken shipping functions. The same test passes every one.</p>
<p>That result should make a green test badge feel less reassuring.</p>
<p>Here is a small experiment you can run in Node.js. The business rule is explicit: shipping costs 500 cents below a subtotal of 5,000 cents, and is free at or above that threshold. All inputs in the experiment are valid nonnegative integer subtotals.</p>
<h2>A plausible test with a large blind spot</h2>
<pre><code class="language-js">import assert from "node:assert/strict";

// Amounts are integer cents. This example uses valid subtotals only.
const variants = {
  reference: s =&gt; s &gt;= 5000 ? 0 : 500,
  wrongBoundary: s =&gt; s &gt; 5000 ? 0 : 500,
  alwaysFree: s =&gt; 0,
  wrongFee: s =&gt; s &gt;= 5000 ? 0 : 5000
};

for (const [name, shipping] of Object.entries(variants)) {
  assert.equal(shipping(6000), 0);
  console.log(name, "PASS");
}
</code></pre>
<p>The output is four passes. The correct implementation passes. So do three incorrect ones: a threshold off by one, free shipping for every order, and a fee ten times too large below the threshold.</p>
<p>Nothing is wrong with <code>assert.equal</code>. The test asks only about an order that qualifies for free shipping. All four implementations answer that particular question correctly.</p>
<h2>Ask the test to distinguish the alternatives</h2>
<p>Keep the 6,000-cent case, then add observations at the boundary and immediately below it:</p>
<pre><code class="language-js">assert.equal(shipping(4999), 500);
assert.equal(shipping(5000), 0);
assert.equal(shipping(6000), 0);
</code></pre>
<p>Running those assertions against the four implementations produces this matrix:</p>
<table>
<thead>
<tr>
<th>Implementation</th>
<th>Original check</th>
<th>Three-case check</th>
</tr>
</thead>
<tbody><tr>
<td>Correct rule</td>
<td>Pass</td>
<td>Pass</td>
</tr>
<tr>
<td><code>&gt;</code> instead of <code>&gt;=</code></td>
<td>Pass</td>
<td>Fail</td>
</tr>
<tr>
<td>Always free</td>
<td>Pass</td>
<td>Fail</td>
</tr>
<tr>
<td>Wrong fee</td>
<td>Pass</td>
<td>Fail</td>
</tr>
</tbody></table>
<p>These are results from the local example, not measurements of a model or a production checkout. The three-case check detects these three changes. It does not establish complete input validation, tax correctness, currency handling or checkout reliability.</p>
<h2>The same assistant can miss the rule twice</h2>
<p>When a coding assistant proposes both an implementation and its tests, inspect which examples it chose. If both artifacts share an omitted boundary, agreement between them tells you little about that boundary.</p>
<p>There is no model comparison hidden in this experiment. A person can write the same weak test. The useful question is whether the test can reject a believable wrong implementation.</p>
<p>A more useful review request is:</p>
<blockquote>
<p>Name three plausible mistakes in this rule. Change the implementation to each one separately, then show which assertion fails.</p>
</blockquote>
<p>That asks for evidence of discrimination. “Add more tests” can produce a longer suite of the same happy path.</p>
<h2>You can start without another test framework</h2>
<p>The deliberate changes here are a small manual exercise. Automated mutation testing applies this idea more broadly. <a href="https://stryker-mutator.io/docs/General/faq/">Stryker's documentation</a> explains the difference between changes that tests detect and changes that survive.</p>
<p>Start with a consequential rule whose expected outcomes you can explain independently: a shipping threshold, a permission decision or an import rejection. Keep the expected behavior separate from the implementation under review.</p>
<p>If no one can explain why an expected value is correct, asking another model to repeat it does not settle the requirement.</p>
<h2>Turn the example into a release check</h2>
<p>Write down the business rule, fixtures, expected outcomes and observed result before release. Keep the owner and unresolved gaps beside the result.</p>
<p>I'm Daniil Kornilov, the author of Kornilov Tools. Launch Control Desk is my local RU/EN browser tool for organizing release checks, risks, owners and supporting evidence. It does not execute the tests; you record the outcomes of checks you perform. Extract the ZIP and open index.html, and export separate JSON backups of your work.</p>
<p>RUB 1,190 for a one-time purchase. No mandatory subscription inside the app.</p>
<p><a href="https://boosty.to/swiftuidev/posts/68708fd5-1dad-4ed3-ac0c-3356d796a00f?utm_source=hashnode&amp;utm_medium=post&amp;utm_campaign=tests_20261024">Buy Launch Control Desk</a>.</p>
<p>Follow this blog for reproducible engineering examples and practical tools for small projects.</p>
]]></content:encoded></item><item><title><![CDATA[Promise.all rejected. Your first write may still complete.]]></title><description><![CDATA[The checkout request failed. The inventory reservation still completed. If your error handler assumes that failure means nothing happened, the retry can be…]]></description><link>https://swiftuidev.hashnode.dev/promise-all-rejected-your-first-write-may-still-complete</link><guid isPermaLink="true">https://swiftuidev.hashnode.dev/promise-all-rejected-your-first-write-may-still-complete</guid><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Daniil Kornilov]]></dc:creator><pubDate>Fri, 23 Oct 2026 14:30:42 GMT</pubDate><content:encoded><![CDATA[<p>The checkout request failed. The inventory reservation still completed.</p>
<p>If your error handler assumes that failure means nothing happened, the retry can be the most dangerous line in the feature.</p>
<p>Consider a hypothetical implementation that starts an inventory reservation and a notification together, then awaits <code>Promise.all</code>. Notification fails. The page shows an error. The reservation is still in flight.</p>
<h2>Reproduce the order without a timer</h2>
<p>This small JavaScript example lets us finish one operation manually after the combined promise has already rejected:</p>
<pre><code class="language-js">const events = [];
let finishReservation;
const reservation = new Promise(resolve =&gt; {
  finishReservation = () =&gt; {
    events.push("stock reserved");
    resolve();
  };
});

try {
  await Promise.all([
    reservation,
    Promise.reject(new Error("notification unavailable"))
  ]);
} catch {
  events.push("request failed");
}

finishReservation();
await reservation;
console.log(events);
// ["request failed", "stock reserved"]
</code></pre>
<p>The resulting event order is the point: a side effect can finish after the caller enters its failure path. The example models that side effect with an array; it does not connect to a real store.</p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all"><code>Promise.all</code></a> rejects when an input rejects. That does not cancel the other operations or undo completed writes. <code>Promise.allSettled</code> can collect every outcome, but it does not provide rollback either.</p>
<h2>Review the state left behind</h2>
<p>For this checkout, I would ask the reviewer to fill in a small failure table:</p>
<table>
<thead>
<tr>
<th>Observation</th>
<th>Question before offering retry</th>
</tr>
</thead>
<tbody><tr>
<td>Reservation succeeded, notification failed</td>
<td>Can notification be retried separately?</td>
</tr>
<tr>
<td>Reservation is still pending</td>
<td>Where can its final state be checked?</td>
</tr>
<tr>
<td>The response was lost</td>
<td>Can the existing operation be identified?</td>
</tr>
<tr>
<td>Both operations succeeded</td>
<td>Which record establishes the completed order?</td>
</tr>
</tbody></table>
<p>“Catch and show a toast” leaves these questions unanswered. The user can see a failure while the server holds a reservation.</p>
<h2>Pick the boundary that must be consistent</h2>
<p>For this example, inventory and order state belong in a server workflow with an explicit consistency strategy. Sending a notification can follow a committed order, with its own delivery status. The exact design depends on which services own the data.</p>
<p>Putting the calls in sequence can prevent a later step from starting too early. It still does not undo a first step when the second fails. A transaction is useful only within the boundary it actually covers; several unrelated HTTP APIs do not become one transaction because they share an <code>await</code>.</p>
<p>If retry is supported, define what identifies the same operation. Check the destination's contract before assuming that repeating a write is safe. A random new identifier on every retry does not identify the original attempt.</p>
<h2>Ask a harder question of generated code</h2>
<p>“Does the happy path work?” is easy to demonstrate. Try this review request instead:</p>
<blockquote>
<p>Fail the second operation after the first has started. Show the final server state, then show what a retry does.</p>
</blockquote>
<p>Run the same exercise on code written by a person or by a coding assistant. The source of the code does not change the failure semantics.</p>
<h2>Keep the failure case in the release record</h2>
<p>For a small project, record the expected behavior, actual result, responsible person and evidence for this scenario before release. That record can live in a document or in a tool your team already uses.</p>
<p>I'm Daniil Kornilov, the author of Kornilov Tools. I offer Launch Control Desk for organizing release checks, risks, owners and evidence in a local RU/EN browser app. Extract the ZIP and open index.html. Keep separate JSON backups of your work. It organizes your checks; it does not run them for you.</p>
<p>The price is RUB 1,190 for a one-time purchase, with no mandatory subscription inside the app.</p>
<p><a href="https://boosty.to/swiftuidev/posts/68708fd5-1dad-4ed3-ac0c-3356d796a00f?utm_source=hashnode&amp;utm_medium=post&amp;utm_campaign=engineering_20261023">Buy Launch Control Desk</a>.</p>
<p>Follow this blog for practical failure examples and reviews of developer workflows.</p>
]]></content:encoded></item><item><title><![CDATA[A CSV comparison should tell you which fields it ignored]]></title><description><![CDATA[Explain CSV comparison rules, preserve text IDs, review mappings, and export clear results with CSV Reconcile Desk.]]></description><link>https://swiftuidev.hashnode.dev/a-csv-comparison-should-tell-you-which-fields-it-ignored</link><guid isPermaLink="true">https://swiftuidev.hashnode.dev/a-csv-comparison-should-tell-you-which-fields-it-ignored</guid><category><![CDATA[data]]></category><dc:creator><![CDATA[Daniil Kornilov]]></dc:creator><pubDate>Thu, 22 Oct 2026 11:42:23 GMT</pubDate><content:encoded><![CDATA[<p>Two exports can compare as unchanged even though a column differs. That result may be correct if the column was deliberately excluded. It becomes misleading when the report hides the selection that produced it.</p>
<p>Here is a fictional example. An older file has <code>sku</code>, <code>price</code> and <code>stock</code>. A newer file has <code>product_id</code>, <code>unit_price</code> and <code>available</code>. You map <code>sku</code> to <code>product_id</code>, then compare only the price fields. A stock change is outside that comparison. A label saying “unchanged” needs that context.</p>
<h2>Put the comparison contract beside the result</h2>
<p>A useful handoff says which columns identify a record, which field pairs participate, and whether whitespace or case is ignored. Record the excluded fields too when they matter to the reader.</p>
<p>That gives “unchanged” a precise meaning: equal on the selected fields under the selected rules. It does not claim that every cell in the source files matches.</p>
<h2>Keep identifiers as text</h2>
<p>An identifier such as <code>001</code> should remain distinguishable from <code>1</code> unless your domain explicitly equates them. Converting both to a number can erase information before the comparison begins.</p>
<p>Duplicate and empty identifiers require their own review. Pairing the first duplicate on one side with the first duplicate on the other can produce a tidy answer without a defensible match.</p>
<h2>Save a reusable mapping, then review it</h2>
<p>If the same pair of export formats arrives every week, a saved mapping can reduce repetitive setup. Apply it to the new headers and check that the intended columns still exist. A renamed or missing field should be visible before you trust the result.</p>
<p>A saved mapping describes a comparison. It is not a backup of the source data. Keep the files needed to reproduce the work according to your own data-handling requirements.</p>
<h2>The tool I offer for this workflow</h2>
<p>I'm Daniil Kornilov, the author of Kornilov Tools. CSV Reconcile Desk is my local browser tool for comparing two CSV/TSV exports by ID. It supports explicit field mappings, optional whitespace and case rules, reusable mapping profiles, and CSV/JSON result exports. It lists ambiguous records separately.</p>
<p>The package includes RU/EN instructions and examples. Extract the ZIP and open index.html on a desktop browser; no package installation or account inside the tool is required. It accepts CSV/TSV rather than XLSX, and uses one ID column. Source CSV data stays in memory; saved mapping profiles use browser storage and can be exported separately.</p>
<p>The price is RUB 990 for a one-time purchase. There is no mandatory subscription inside the tool.</p>
<p><a href="https://boosty.to/swiftuidev/posts/5e3a3e7d-0796-4152-bdae-5696dcfbd499?utm_source=hashnode&amp;utm_medium=post&amp;utm_campaign=news_20261022">Buy CSV Reconcile Desk</a>.</p>
<p>Start with one familiar pair of exports. Check the mapping, inspect the ambiguous records, and hand over the comparison rules with the report.</p>
<p>Follow this blog for practical examples of working with data, developer tools and client projects.</p>
]]></content:encoded></item><item><title><![CDATA[A client change request needs a decision record, not just a price]]></title><description><![CDATA[A practical change-request example for freelance developers: define scope, estimate the fee, record approval, and keep payment separate.]]></description><link>https://swiftuidev.hashnode.dev/a-client-change-request-needs-a-decision-record-not-just-a-price</link><guid isPermaLink="true">https://swiftuidev.hashnode.dev/a-client-change-request-needs-a-decision-record-not-just-a-price</guid><category><![CDATA[Freelancing]]></category><dc:creator><![CDATA[Daniil Kornilov]]></dc:creator><pubDate>Sun, 18 Oct 2026 17:42:46 GMT</pubDate><content:encoded><![CDATA[<p>A client asks for a pricing section on a website. The original scope did not include it. A useful response has to describe the new deliverable, its price, its effect on the schedule, and the decision needed before work begins.</p>
<p>Here is a fictional example of that workflow, followed by the tool I offer for keeping those records. The figures are examples, not a client result.</p>
<h2>Write the boundary before the estimate</h2>
<p>A request such as “improve the landing page” is difficult to close. Try: “Add three pricing cards to the existing page, using the current visual style and copy supplied by the client.”</p>
<p>Record exclusions beside it. Payment integration, new illustrations, and rewriting the copy are separate changes in this example. The estimate can then refer to an identifiable piece of work.</p>
<h2>Separate working time from the delivery date</h2>
<p>Suppose the estimate is three hours at an agreed additional-work rate of RUB 1,000 per hour. The proposed fee is RUB 3,000. If the existing schedule requires moving delivery by one working day, include that explicitly. Three working hours and one calendar-day change describe different constraints.</p>
<p>A request could read:</p>
<blockquote>
<p>Add three pricing cards using the existing design and your final copy. Estimated work: three hours. Additional fee: RUB 3,000. Delivery moves by one working day. Payment integration and new illustrations are excluded. Please confirm the scope, fee, and revised delivery date before I begin.</p>
</blockquote>
<p>Use your own estimate and agreed terms. Unknown requirements should become questions before they become a fixed quote.</p>
<h2>Keep the estimate, decision, and payment distinct</h2>
<p>A sent estimate belongs in a pending state. It should not silently increase the agreed project budget. When the client accepts it, save their confirmation and record the decision. Payment remains a separate observation.</p>
<p>That distinction matters in an interface too. A status called “approved” should explain what has been approved. Treating it as proof of payment makes a project summary misleading.</p>
<p>For several requests, keep the original scope, each change, its estimate, the client's decision, and the confirmation together. That gives you something concrete to inspect before preparing the next message.</p>
<h2>A ready-to-use option: Scope Change Desk</h2>
<p>Scope Change Desk is my offline tool for recording fixed-price project terms, estimating changes, and exporting an individual approval request. It supports up to 100 local projects and includes RU/EN instructions, examples, and a separate Excel workbook with formulas. The browser app and workbook are independent; they do not sync. Keep exported JSON backups of your projects.</p>
<p>The one-time price is RUB 790, with no required in-app subscription. <a href="https://boosty.to/swiftuidev/posts/7175948d-3f36-46e8-8553-8109a21d79cb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=scope_20261018">Buy Scope Change Desk</a>.</p>
<p>Use it to prepare the request and keep the record. Sending the message, obtaining the client's decision, and confirming payment remain your responsibility.</p>
<p>Follow this blog for practical examples of developer tools, local browser workflows, and Kornilov Tools updates.</p>
]]></content:encoded></item><item><title><![CDATA[GitHub star history: an aggregate endpoint is a different product contract]]></title><description><![CDATA[GitHub redesigned star history. A practical way to review the question, sample responses and missing-data behaviour of a dashboard API.]]></description><link>https://swiftuidev.hashnode.dev/github-star-history-an-aggregate-endpoint-is-a-different-product-contract</link><guid isPermaLink="true">https://swiftuidev.hashnode.dev/github-star-history-an-aggregate-endpoint-is-a-different-product-contract</guid><category><![CDATA[api]]></category><dc:creator><![CDATA[Daniil Kornilov]]></dc:creator><pubDate>Fri, 16 Oct 2026 09:58:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/697f963120e4834274ec287b/2a1fb024-5ad2-4e53-996c-c9683d937a81.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>On October 14, GitHub described its redesigned star-history API. In its <code>react/react</code> example, retrieving the curve takes about 24 requests instead of about 2,511. The new response contains daily counts across up to 30 weeks per request, without stargazer identities. Those figures describe GitHub's example, not a benchmark I ran. <a href="https://github.blog/engineering/architecture-optimization/from-2500-requests-to-24-rebuilding-star-history-without-the-stargazers/">Engineering write-up</a>.</p>
<p>That change is useful to think about when designing a dashboard API. Start with the question the screen must answer, then decide what data the client should receive.</p>
<h2>Define the decision before the payload</h2>
<p>Consider a fictional dashboard for a small project. Its owner wants to compare interest before and after a release. A useful product brief would state the comparison window, date resolution and acceptable delay before a new observation appears.</p>
<p>It should also say what the chart does not prove. A rise in interest is not evidence of paid demand, and an aggregate chart cannot explain why an individual person acted. Those are different questions requiring different evidence.</p>
<h2>Review a response as a contract</h2>
<p>Before implementing a new endpoint, I would ask the team to approve three example responses: an ordinary period, an empty period and a period where collection failed. The interface should not silently draw the same zero-height line for all three.</p>
<p>Then inspect the client's responsibilities. Does it have enough context to label the time window? Can it tell a complete result from a partial one? Does the screen explain what the count represents? These are product questions even when their answers appear in a JSON schema.</p>
<h2>Turn the design into a reviewable experiment</h2>
<p>Use a fictional fixture with a release date and known observations. Ask a reviewer to explain the chart without reading the implementation. If they mistake an unavailable period for no activity, change the response or presentation before adding more visual polish.</p>
<p>Keep a short decision note with the intended question, sample response and known gaps. That note gives later changes a useful test: does the new contract still let a reader make the intended comparison? A compact payload is valuable when its meaning survives the trip to the screen.</p>
]]></content:encoded></item><item><title><![CDATA[Your demo button should not overwrite someone’s work]]></title><description><![CDATA[A demo button is easy to add to a browser tool. Keeping it from replacing someone's draft takes a more deliberate design. Consider a small case-study…]]></description><link>https://swiftuidev.hashnode.dev/your-demo-button-should-not-overwrite-someone-s-work</link><guid isPermaLink="true">https://swiftuidev.hashnode.dev/your-demo-button-should-not-overwrite-someone-s-work</guid><dc:creator><![CDATA[Daniil Kornilov]]></dc:creator><pubDate>Thu, 15 Oct 2026 13:04:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/697f963120e4834274ec287b/e3f3ee9b-f051-4c95-ae24-f3953efd5405.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A demo button is easy to add to a browser tool. Keeping it from replacing someone's draft takes a more deliberate design.</p>
<p>Consider a small case-study editor. A visitor has written a project description, then clicks “View an example”. If the implementation copies sample text into the same object that autosave uses, the next save can replace the visitor's work with fictional content.</p>
<h2>Separate the working draft from the displayed example</h2>
<p>In the Kornilov Tools free case editor, the working draft and the current display mode are separate. The display chooses either a newly created fictional example or the working draft. Opening an example does not import it into the working object.</p>
<p>This makes the boundary explicit: viewing an example is a navigation action; replacing work is a different operation. The example is also labelled fictional in exported output.</p>
<h2>Treat import as a replacement workflow</h2>
<p>A JSON file should be parsed and validated before it becomes the working draft. In this editor, selecting a valid backup opens a confirmation dialog. Continuing first initiates a download of the current draft, then applies the replacement. Cancelling leaves the draft unchanged.</p>
<p>That downloaded copy is a safeguard, not a storage guarantee: the browser can still block a download, and the user needs to keep the file. The interface should make that responsibility understandable.</p>
<h2>Preserve unreadable data</h2>
<p>A saved string that fails parsing is not an empty draft. The editor keeps the original string available for rescue and blocks ordinary saves while recovery is pending. Otherwise, an innocent input event could replace the only remaining copy with a new blank object.</p>
<p>Storage itself can also fail. A local-only interface should show that saving is unavailable while keeping the visible text available for export.</p>
<h2>Describe this as a technical case</h2>
<p>The useful portfolio claim is the behaviour, not “improved user experience”. A concrete review scenario is:</p>
<ol>
<li>Write a draft with a recognisable title.</li>
<li>Open the fictional example, then return to work.</li>
<li>Check that the title remains unchanged.</li>
<li>Select malformed JSON and check that it does not replace the draft.</li>
<li>Import a valid backup, cancel once, then confirm and inspect the downloaded previous draft.</li>
</ol>
<p>These are suggested checks for the failure paths, not evidence that every browser or storage condition has been tested. A screenshot of a successful import alone cannot establish all of them.</p>
<p>A strong case study names the state boundary, explains the rejected shortcut, and shows what evidence would establish the claimed behaviour. That gives a reader something more useful than a list of frameworks.</p>
]]></content:encoded></item><item><title><![CDATA[Turn one repository into a portfolio case people can understand]]></title><description><![CDATA[Opening a repository should not be the only way to learn what your project does. Give the reader a short route through one case: the problem, your role, a…]]></description><link>https://swiftuidev.hashnode.dev/turn-one-repository-into-a-portfolio-case-people-can-understand</link><guid isPermaLink="true">https://swiftuidev.hashnode.dev/turn-one-repository-into-a-portfolio-case-people-can-understand</guid><dc:creator><![CDATA[Daniil Kornilov]]></dc:creator><pubDate>Tue, 13 Oct 2026 18:03:17 GMT</pubDate><enclosure url="https://images.boosty.to/image/4eb56630-ff7d-446a-8292-a69c02837bdd" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Opening a repository should not be the only way to learn what your project does.</p>
<p>Give the reader a short route through one case: the problem, your role, a technical decision, and something they can inspect. A screenshot explains the interface. A small diagram explains the boundary. A test or reproducible demo supports the result.</p>
<p>For a personal project, say it is a personal project. Explain the constraint you chose: local storage, a small input limit, no server, or a narrow audience. Those choices can lead to a useful discussion even without paying customers.</p>
<p>One detail is worth including: what you intentionally left out and why. “I used one stable ID rather than fuzzy matching because ambiguous matches needed manual review” reveals more judgment than a row of library badges.</p>
<p>Check the case on a phone. Can someone understand the project before following a link? Is the repository easy to find? Does the demo work without your development environment?</p>
<p><strong>Portfolio Case Kit</strong> is my local editor for assembling these cases and exporting a standalone portfolio site or GitHub README. You write the project details; the tool handles the structure and export.</p>
<p><a href="https://pease163.github.io/digital-products/en/products/portfolio-case.html?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=kornilov_tools&amp;utm_content=portfolio-case">See the portfolio kit</a>.</p>
]]></content:encoded></item><item><title><![CDATA[10 SwiftUI Performance Tips for 2026]]></title><description><![CDATA[SwiftUI is fast by default, but knowing these optimization techniques will keep your apps buttery smooth. For long lists, lazy loading is essential:]]></description><link>https://swiftuidev.hashnode.dev/10-swiftui-performance-tips-for-2026</link><guid isPermaLink="true">https://swiftuidev.hashnode.dev/10-swiftui-performance-tips-for-2026</guid><category><![CDATA[SwiftUI]]></category><dc:creator><![CDATA[Daniil Kornilov]]></dc:creator><pubDate>Sun, 01 Feb 2026 18:20:10 GMT</pubDate><content:encoded><![CDATA[<p>SwiftUI is fast by default, but knowing these optimization techniques will keep your apps buttery smooth.</p>
<ol>
<li><h2 id="heading-use-lazyvstack-instead-of-vstack">Use LazyVStack Instead of VStack</h2>
</li>
</ol>
<p>For long lists, lazy loading is essential:</p>
<pre><code class="lang-plaintext">swift
// Bad - loads all items at once
ScrollView {
    VStack {
        ForEach(items) { item in
            ItemRow(item: item)
        }
    }
}

// Good - loads only visible items
ScrollView {
    LazyVStack {
        ForEach(items) { item in
            ItemRow(item: item)
        }
    }
}
```

## 2. Avoid Expensive Operations in body

Move calculations outside the view body:

```swift
struct GoodView: View {
    let processedData: [Item] // Pre-computed

    var body: some View {
        List(processedData) { item in
            Text(item.name)
        }
    }
}
```

## 3. Use @StateObject for View-Owned Objects

```swift
// Correct - object persists across re-renders
@StateObject private var viewModel = ViewModel()

// Wrong - creates new instance on each render
@ObservedObject var viewModel = ViewModel()
```

## 4. Minimize View Hierarchy Depth

Flatten nested views when possible.

## 5. Use EquatableView for Complex Views

```swift
struct HeavyView: View, Equatable {
    let data: ComplexData

    static func == (lhs: Self, rhs: Self) -&gt; Bool {
        lhs.data.id == rhs.data.id
    }

    var body: some View {
        // Complex rendering
    }
}

// Usage
EquatableView(content: HeavyView(data: data))
```

## 6. Use drawingGroup() for Complex Graphics

```swift
ComplexShape()
    .drawingGroup() // Renders to Metal
```

## 7. Profile with Instruments

Always measure before optimizing!

---

**Want pre-optimized components?** Check out my [SwiftUI UI Components Pack](https://boosty.to/swiftuidev) — 38 components built with performance in mind!
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Async/Await Networking in Swift: From Zero to API Calls]]></title><description><![CDATA[Modern Swift networking is clean, readable, and powerful thanks to async/await. Let’s build a complete networking layer from scratch. Swift’s URLSession…]]></description><link>https://swiftuidev.hashnode.dev/asyncawait-networking-in-swift-from-zero-to-api-calls</link><guid isPermaLink="true">https://swiftuidev.hashnode.dev/asyncawait-networking-in-swift-from-zero-to-api-calls</guid><category><![CDATA[Swift]]></category><dc:creator><![CDATA[Daniil Kornilov]]></dc:creator><pubDate>Sun, 01 Feb 2026 18:18:15 GMT</pubDate><content:encoded><![CDATA[<p>Modern Swift networking is clean, readable, and powerful thanks to async/await. Let’s build a complete networking layer from scratch.</p>
<h2 id="heading-the-foundation-urlsession-asyncawait">The Foundation: URLSession + async/await</h2>
<p>Swift’s URLSession now supports async/await natively:</p>
<pre><code class="lang-plaintext">swift
func fetchData() async throws -&gt; Data {
    let url = URL(string: "https://api.example.com/users")!
    let (data, response) = try await URLSession.shared.data(from: url)

    guard let httpResponse = response as? HTTPURLResponse,
          httpResponse.statusCode == 200 else {
        throw NetworkError.invalidResponse
    }

    return data
}
```

## Building a Reusable Network Service

Here's a production-ready networking layer:

```swift
actor NetworkService {
    static let shared = NetworkService()
    private let decoder = JSONDecoder()

    func fetch&lt;T: Decodable&gt;(_ type: T.Type, from url: URL) async throws -&gt; T {
        let (data, response) = try await URLSession.shared.data(from: url)

        guard let httpResponse = response as? HTTPURLResponse else {
            throw NetworkError.invalidResponse
        }

        guard 200...299 ~= httpResponse.statusCode else {
            throw NetworkError.statusCode(httpResponse.statusCode)
        }

        return try decoder.decode(T.self, from: data)
    }
}

enum NetworkError: Error {
    case invalidResponse
    case statusCode(Int)
    case decodingFailed
}
```

## Using It in SwiftUI

```swift
struct ContentView: View {
    @State private var users: [User] = []

    var body: some View {
        List(users) { user in
            Text(user.name)
        }
        .task {
            do {
                let url = URL(string: "https://api.example.com/users")!
                users = try await NetworkService.shared.fetch([User].self, from: url)
            } catch {
                print("Error: \(error)")
            }
        }
    }
}
```

## Key Benefits

- **No callbacks** — linear, readable code
- **Built-in cancellation** — automatic with SwiftUI's .task
- **Actor isolation** — thread-safe by design

---

**Need a complete networking setup?** My [SwiftUI Starter Kit Pro](https://boosty.to/swiftuidev) includes a full NetworkManager with error handling, caching, and retry logic!
</code></pre>
]]></content:encoded></item><item><title><![CDATA[SwiftUI MVVM Architecture: A Complete Guide for 2026]]></title><description><![CDATA[MVVM (Model-View-ViewModel) is the go-to architecture for building maintainable SwiftUI applications. In this guide, I’ll show you how to implement it…]]></description><link>https://swiftuidev.hashnode.dev/swiftui-mvvm-architecture-a-complete-guide-for-2026</link><guid isPermaLink="true">https://swiftuidev.hashnode.dev/swiftui-mvvm-architecture-a-complete-guide-for-2026</guid><category><![CDATA[SwiftUI]]></category><dc:creator><![CDATA[Daniil Kornilov]]></dc:creator><pubDate>Sun, 01 Feb 2026 18:10:10 GMT</pubDate><content:encoded><![CDATA[<p>MVVM (Model-View-ViewModel) is the go-to architecture for building maintainable SwiftUI applications. In this guide, I’ll show you how to implement it properly using the modern @Observable macro.</p>
<h2 id="heading-why-mvvm-for-swiftui">Why MVVM for SwiftUI?</h2>
<p>SwiftUI’s declarative nature pairs perfectly with MVVM:</p>
<ul>
<li><p><strong>Model</strong>: Your data structures</p>
</li>
<li><ul>
<li><p><strong>View</strong>: SwiftUI views that display UI</p>
<ul>
<li><ul>
<li><strong>ViewModel</strong>: Connects Model to View, handles business logic</li>
</ul>
</li>
<li><h2 id="heading-basic-implementation">Basic Implementation</h2>
</li>
</ul>
</li>
</ul>
</li>
<li><p>Here’s a clean MVVM setup:</p>
</li>
</ul>
<pre><code class="lang-plaintext">swift
import SwiftUI

// MARK: - Model
struct User: Identifiable, Codable {
    let id: UUID
    var name: String
    var email: String
}

// MARK: - ViewModel
@Observable
class UserViewModel {
    var users: [User] = []
    var isLoading = false
    var errorMessage: String?

    func fetchUsers() async {
        isLoading = true
        defer { isLoading = false }

        do {
            users = try await NetworkService.shared.fetch()
        } catch {
            errorMessage = error.localizedDescription
        }
    }
}

// MARK: - View
struct UserListView: View {
    @State private var viewModel = UserViewModel()

    var body: some View {
        List(viewModel.users) { user in
            Text(user.name)
        }
        .task {
            await viewModel.fetchUsers()
        }
    }
}
```

## Key Benefits

1. **Testability** — ViewModel can be unit tested independently
2. **Separation of concerns** — UI logic stays in ViewModel
3. **Reusability** — Same ViewModel can power different views

---

**Want a complete MVVM starter project?** Check out my [SwiftUI Starter Kit Pro](https://boosty.to/swiftuidev) with 5 ready-to-use screens and full MVVM architecture!
</code></pre>
]]></content:encoded></item></channel></rss>