<?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" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Ali Kamalizade on Medium]]></title>
        <description><![CDATA[Stories by Ali Kamalizade on Medium]]></description>
        <link>https://medium.com/@ali-dev?source=rss-3a534b5053e6------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*VfBKjUuDIKWx-OZuMvcvSw.jpeg</url>
            <title>Stories by Ali Kamalizade on Medium</title>
            <link>https://medium.com/@ali-dev?source=rss-3a534b5053e6------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 05 Apr 2026 21:45:31 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@ali-dev/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[How to Fix MacBook Touch Bar not Showing Up]]></title>
            <link>https://ali-dev.medium.com/how-to-fix-macbook-touch-bar-not-showing-up-71563859c4b3?source=rss-3a534b5053e6------2</link>
            <guid isPermaLink="false">https://medium.com/p/71563859c4b3</guid>
            <category><![CDATA[apple]]></category>
            <category><![CDATA[macbook]]></category>
            <category><![CDATA[macos]]></category>
            <category><![CDATA[touch-bar]]></category>
            <category><![CDATA[mac]]></category>
            <dc:creator><![CDATA[Ali Kamalizade]]></dc:creator>
            <pubDate>Wed, 25 Feb 2026 21:58:56 GMT</pubDate>
            <atom:updated>2026-02-25T21:58:56.782Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="MacBook with Touch Bar" src="https://cdn-images-1.medium.com/max/1024/0*kZRC_MykL-GKvrDt.jpeg" /></figure><p>I use a MacBook Pro M1. The laptop works well for most tasks such as browsing or programming. I kind of miss the Touch Bar that Apple introduced and has discontinued a few years ago. Definitely gimmicky but not the worst gimmick in my opinion and sometimes neat functionality.</p><p>Somehow, the Touch Bar recently disappeared on my MacBook without warning. As the MacBook is still working fine I did not want to get rid of it yet. Hence I looked into what it takes to get the Touch Bar to work again.</p><p>Of course, there are the usual suspects:</p><ul><li>Restart the laptop</li><li>Update MacOS to the latest version</li><li>Go to System Settings -&gt; Keyboard -&gt; Touch Bar</li><li>Kill the Touch Bar process using the Activity Monitor</li></ul><p>If any of these steps did the trick for you: great! However, these things did not help me in my situation. I was afraid that it was a hardware issue. Instead, the following approach describes how I actually fixed the Touch Bar on my MacBook:</p><ol><li>Turn off the MacBook completely</li><li>Start the MacBook and keep the power button pressed until you get to the boot menu</li><li>Press Shift, then select the volume (usually called “Macintosh HD”) and then select “Safe Boot”</li><li>Verify that you are in Safe Mode (e.g. the top menu bar on the login screen may say so). Login normally with your credentials in Safe Mode.</li><li>Your Touch bar may now work again. Turn off your MacBook and restart normally without Safe Mode.</li></ol><figure><img alt="Safe Mode boot" src="https://cdn-images-1.medium.com/max/940/0*5ojbF8NO85mxxGfH" /></figure><p>It seems that the Safe Mode made the MacBook “remember” that it has a Touch Bar. Basically, it turned out to be yet another case of <em>“a reboot fixes any issue”</em> that IT people preach (for good reason) — but in this case, it needed a very particular kind of reboot 😄</p><p>In case it is still not working and you also tried the usual suspects mentioned above, then it might indeed be a hardware failure and the only thing that you can do is to let a professional repair it.</p><h4>Conclusion</h4><p>Thanks for reading this short post. I for one am glad that it was merely a software problem and not a hardware issue. Still, I wish this information would have showed up quicker in ChatGPT/Gemini/Google which gave me motivation to turn this into a quick blog post. Let me know in the comments in case you had a similar issue.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=71563859c4b3" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to make password inputs show asterisks when running sudo commands]]></title>
            <link>https://ali-dev.medium.com/how-to-make-password-inputs-show-asterisks-when-running-sudo-commands-e222b618129a?source=rss-3a534b5053e6------2</link>
            <guid isPermaLink="false">https://medium.com/p/e222b618129a</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[linux]]></category>
            <category><![CDATA[apple]]></category>
            <category><![CDATA[mac]]></category>
            <dc:creator><![CDATA[Ali Kamalizade]]></dc:creator>
            <pubDate>Mon, 02 Feb 2026 10:56:01 GMT</pubDate>
            <atom:updated>2026-02-02T10:56:01.751Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*5qDd4sGI3VjdvIeS" /><figcaption>Photo by <a href="https://unsplash.com/@markusspiske?utm_source=medium&amp;utm_medium=referral">Markus Spiske</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>When you run a command with sudo in MacOS or Linux, the terminal prompts you to type in your password and it doesn&#39;t give you any visual feedback.</p><p>I am a fast typer, so when I mess up my password, I have to start over from scratch. Also, it sometimes happens that I am not even sure that something is happening while typing. I am fine with not showing the password while typing but it would be neat to see asterisks like in regular password fields. Luckily, it turns out it is easier than I initially thought.</p><p>Here is a quick tweak that will bring back those familiar asterisks (*) when you type in your password. We will use vi but other editors like nano should work as well. To bring back those asterisks, we can do the following:</p><ol><li>Run the following command in a Terminal: sudo visudo</li><li>Scroll down to the line that looks like this: Defaults env_reset</li><li>Change it to this: Defaults env_reset,pwfeedback . Optional: you can provide a comment so that your future self remembers why you changed this setting in the first place.</li><li>Exit and save the changes. Mac users, for example, may use vi and will have to type :wq and press Enter to exit.</li><li>Restart the terminal and run a command requiring sudo: you should now see asterisks when typing.</li></ol><h4>Security Implications</h4><p>Yes, there is a security risk, though it is relatively minor in most scenarios from what I can think of. Here is a breakdown of what changes when you enable pwfeedback.</p><p><strong>Shoulder Surfing: </strong>One reason terminal passwords are invisible by default is to prevent someone standing behind you from seeing the length of your password.</p><ul><li>Without asterisks: A bystander has no idea if your password is 10 characters or 40.</li><li>With asterisks: They can count the characters. This narrows down the “search space” if someone were trying to brute-force or guess your password later.</li></ul><p><strong>Systematic Vulnerability:</strong> In very high-security environments, even the <em>timing</em> of those asterisks appearing could theoretically be used in a “side-channel attack” to guess characters based on typing rhythm, though this is overkill for most regular users.</p><h4>Caveats &amp; Limitations</h4><p>The only caveat I ran into is that this will not work everywhere. I will use Apple’s FileVault mechanism as an example. The reason it is not working there is that FileVault (and the fdesetup command) operates on a different security layer than standard sudo commands.</p><p>When you run a standard command like sudo brew install, the sudo utility is what asks for your password. Since you edited the sudoers file, sudo knows to show you the asterisks.</p><p>However, when you run sudo fdesetup, two things happen:</p><ol><li>sudo asks for your admin password (this should show asterisks).</li><li>Once authorized, the fdesetup tool takes over and asks for your FileVault Recovery Key or password.</li></ol><p>At that second step, fdesetup is talking directly to the macOS Disk Management framework, which does not care about your sudoers settings. It has its own hard-coded security behavior.</p><p>FileVault handles encryption at the disk level. Apple intentionally keeps these prompts as “sterile” and feedback-free as possible. I’d think it’s because FileVault passwords and recovery keys are the “keys to the kingdom” for your entire hard drive, macOS doesn’t allow user-level configuration files (like sudoers) to change how that data is handled or displayed.</p><h4>Conclusion</h4><p>Overall, this is a nice quality of life change that I wish I knew earlier. Even though the caveat mentioned above is a bit annoying, I appreciate that working with sudo becomes easier by seeing the asterisks.</p><p>Any other near tricks that come to mind when working with the terminal? Let me know in the comments.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e222b618129a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[My 11 Favorite ESLint Rules]]></title>
            <link>https://ali-dev.medium.com/my-11-favorite-eslint-rules-1cfd66667b6f?source=rss-3a534b5053e6------2</link>
            <guid isPermaLink="false">https://medium.com/p/1cfd66667b6f</guid>
            <category><![CDATA[eslint]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Ali Kamalizade]]></dc:creator>
            <pubDate>Mon, 05 Jan 2026 15:46:44 GMT</pubDate>
            <atom:updated>2026-01-05T21:49:16.462Z</atom:updated>
            <content:encoded><![CDATA[<h4>Handy rules to adhere to keep adhering to good practices for developers and AI tools</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*-eBGA2hjSSrqehK2" /><figcaption>Photo by <a href="https://unsplash.com/@2mduffel?utm_source=medium&amp;utm_medium=referral">Mark Duffel</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>At Sunhat, we spend a lot of time in TypeScript. Between technologies like Angular, NestJS, and Nx, there’s a lot of code moving fast across a SaaS product. The bigger a codebase grows, the more you rely on the small invisible things that keep it maintainable. ESLint is one of those things.</p><p>I’ve come to see ESLint not just as a linter but as a guardrail system. It keeps you from slowly drifting into chaos. We have a custom ESLint setup that evolved over time, and there are a few rules that have proven helpful to how we work.</p><p>This not only applies to the engineers themselves but also to AI tools like Cursor. We use tools like Cursor increasingly for refactorings, quick iterations, and exploring changes. When your codebase has strict, automated constraints, the AI doesn’t just write code: it writes &quot;your&quot; code. It is less hallucinating patterns that don’t exist and starts following your architecture and good practices.</p><p>ESLint rules effectively teach them how your code is supposed to look. If not already done automatically, you can let your AI tools make use of ESLint to validate itself. Hence the AI has far fewer degrees of freedom when it’s generating code. That’s a good thing.</p><p>These are the categories of rules we’ll be looking at:</p><ul><li>Avoiding to delete temporary code</li><li>Prefer</li><li>Avoid common bugs</li><li>Consistency</li></ul><p>Without further ado, let’s have a look.</p><h4>1. @nrwl/nx/enforce-module-boundaries</h4><p>If you use Nx, this rule is non-negotiable. We treat our Nx workspaces as modular systems. Each app and library has a clear domain. Without boundaries, developers will eventually take shortcuts — importing code from where it’s convenient rather than where it belongs.</p><p>This rule prevents that. It enforces dependency constraints between libraries, ensures each domain stays self-contained, and stops circular imports before they ever happen. Every time it blocks a “quick import” across layers, it saves you from future architectural debt.</p><p>Here is an example:</p><pre>&#39;@nx/enforce-module-boundaries&#39;: [<br>    &#39;error&#39;,<br>    {<br>        allow: [],<br>        depConstraints: [{<br>                sourceTag: &#39;type:app&#39;,<br>                onlyDependOnLibsWithTags: [&#39;type:feature&#39;, &#39;type:shared&#39;]<br>            },<br>            {<br>                sourceTag: &#39;type:feature&#39;,<br>                onlyDependOnLibsWithTags: [&#39;type:shared&#39;, &#39;type:feature&#39;]<br>            },<br>            {<br>                sourceTag: &#39;type:shared&#39;,<br>                onlyDependOnLibsWithTags: [&#39;type:shared&#39;]<br>            }<br>        ]<br>    }<br>]</pre><h4>2. @typescript-eslint/member-ordering</h4><p>Codebases aren’t just about what they do, but how they feel when you read them. This rule enforces consistent ordering of class members: constructors, lifecycle hooks, public methods, private helpers, and so on. You don’t have to wonder where to find things.</p><pre>&#39;@typescript-eslint/member-ordering&#39;: [&#39;warn&#39;, {<br>    default: [&#39;field&#39;, &#39;get&#39;, &#39;constructor&#39;, &#39;method&#39;, &#39;signature&#39;]<br>}]</pre><h4>3. @typescript-eslint/naming-convention</h4><p>Naming consistency is one of those things you don’t appreciate until it’s gone.</p><p>This rule enforces our conventions for classes, interfaces, constants, and private members. It’s the kind of detail that prevents friction: like whether we prefix private fields with <em>_</em> or not, or if interfaces should start with <em>I</em>. More importantly, it makes code reviews shorter because we no longer have to discuss naming at all.</p><pre> &#39;@typescript-eslint/naming-convention&#39;: [&#39;error&#39;, {<br>     selector: &#39;default&#39;,<br>     format: [&#39;camelCase&#39;]<br> }, {<br>     selector: [&#39;class&#39;, &#39;interface&#39;, &#39;typeParameter&#39;, &#39;typeAlias&#39;],<br>     format: [&#39;PascalCase&#39;]<br> }, {<br>     selector: [&#39;objectLiteralProperty&#39;, &#39;objectLiteralMethod&#39;],<br>     format: null<br> }, {<br>     selector: [&#39;enum&#39;, &#39;enumMember&#39;],<br>     format: [&#39;PascalCase&#39;, &#39;snake_case&#39;]<br> }, {<br>     selector: &#39;parameter&#39;,<br>     format: [&#39;camelCase&#39;],<br>     leadingUnderscore: &#39;allow&#39;<br> }, {<br>     selector: &#39;variable&#39;,<br>     format: [&#39;UPPER_CASE&#39;, &#39;camelCase&#39;],<br>     leadingUnderscore: &#39;allow&#39;<br> }, {<br>     selector: &#39;typeProperty&#39;,<br>     format: null,<br>     filter: {<br>         regex: &#39;^_count$&#39;,<br>         match: true<br>     }<br> }]</pre><h4>4. @typescript-eslint/prefer-optional-chain</h4><p>Instead of deeply nested null checks, you just write foo?.bar?.baz. It reads like a sentence and eliminates a ton of boilerplate.</p><p>We enforce it to keep code clean and expressive — and to avoid the old “cannot read property of undefined” errors that everyone loves.</p><h4>5. @typescript-eslint/prefer-nullish-coalescing</h4><p>This rule complements the previous one. It enforces ?? instead of || when dealing with default values.</p><p>Why? Because 0 and &quot; are valid values. With ||, they’re treated as falsy, and you might accidentally override them. ?? fixes that by only falling back on null or undefined. Small difference that can prevent easy-to-miss-bugs.</p><h4>6. @typescript-eslint/switch-exhaustiveness-check</h4><p>If you’re switching over a union type, this rule ensures all possible cases are handled. If a new variant is added later and you forget to handle it, then ESLint reminds you.</p><p>For us, this is crucial in large enums and discriminated unions that evolve as our business logic grows.</p><h4>7. etc/no-commented-out-code</h4><p>Dead code is noise. Commented-out code is lying noise: it suggests something might matter when it doesn’t. We don’t leave commented code behind. If it’s useful, it belongs in Git history. If it’s not, delete it.</p><p>This rule keeps our diffs smaller and our minds clearer.</p><h4>8. lodash-fp/use-fp</h4><p>We use lodash/fp instead of the classic lodash API. The functional version promotes immutability and better composability.</p><p>This rule enforces that all lodash imports come from lodash/fp. It prevents accidental mutable operations that could mess with state.</p><h4>9. no-console</h4><p>Logging is great. But stray console.logs in production code aren’t.<br>We use structured logging through proper monitoring and telemetry systems (Sentry, platform logs, etc.).</p><p>This rule blocks console.log and similar methods from creeping into production code while allowing explicit exceptions where necessary.</p><h4>10. no-param-reassign</h4><p>Mutating function parameters is one of those small bad habits that quietly leads to bugs.</p><p>We want our functions to be predictable and side-effect free.<br>This rule forces you to create new variables instead of mutating inputs. It makes data flow more explicit and debugging much easier.</p><h4>11. prefer-const</h4><p>If a variable never changes, it should be const. This rule is simple, but it’s one of those that make JavaScript feel more like a safe, typed language.</p><p>const means: “<em>this won’t change</em>.” It signals intent and thereby reduces mental load.</p><h4>Conclusion</h4><p>Our ESLint config is a small example of our philosophy in action. It encodes what we care about: safety, clarity, and consistency. And when those things are built into your tooling, your team gets to focus on what really matters: building. Of course it’s up to your team to decide which rules make sense. And if existing rules don’t cover it: why not create your own?</p><p>Thanks for reading this post. Let me know in the comments about useful tools and rules you are using.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1cfd66667b6f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Making GitHub Actions Fast(er) & Cheaper with Dedicated Runners]]></title>
            <link>https://ali-dev.medium.com/making-github-ci-cd-fast-er-cheaper-with-dedicated-runners-55612586afd7?source=rss-3a534b5053e6------2</link>
            <guid isPermaLink="false">https://medium.com/p/55612586afd7</guid>
            <category><![CDATA[github-actions]]></category>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[continuous-integration]]></category>
            <dc:creator><![CDATA[Ali Kamalizade]]></dc:creator>
            <pubDate>Wed, 17 Dec 2025 09:42:16 GMT</pubDate>
            <atom:updated>2025-12-17T22:08:34.802Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*dMbaNz5ggeDNv_iq" /><figcaption>Photo by <a href="https://unsplash.com/@afgprogrammer?utm_source=medium&amp;utm_medium=referral">Mohammad Rahmani</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>If you’ve spent any time staring at slow CI runs on GitHub Actions, you know the pain: minutes tick away while runners spin up and tests crawl.</p><p>We’ve been chipping away at this for a while, constantly tweaking caching and optimizing job dependencies. But we recently hit a ceiling that came down to the simplest piece of infrastructure: the runners themselves.</p><p>Even when you pay for beefier GitHub hardware, per-minute pricing adds up quickly — and the default GitHub runners may still not keep up with how fast teams like us want to iterate.</p><p>The goal was simple: keep all the goodness of GitHub Actions (YAML workflows, integrations, secrets, triggers), but run them on hardware and tooling that gives you better performance per dollar. Hence we started to use <strong>Blacksmith</strong> as a drop-in replacement for GitHub’s hosted runners.</p><h4>Benefits</h4><p>Here’s what we found when we adopted Blacksmith:</p><ul><li><strong>Significant speedups</strong> — Blacksmith runs Actions on high-performance machines with better throughput than GitHub’s default runners and even faster than their dedicated runners with the same hardware configuration, so builds and tests finish noticeably faster.</li><li><strong>Faster runs, lower bills:</strong> Blacksmith advertises ~2× faster execution and ~50% cheaper per‑minute cost than GitHub hosted runners, which in practice can cut your CI bill by up to ~75%. Your mileage may vary but we are seeing good improvements.</li><li><strong>Plug‑and‑play adoption:</strong> For most workflows, you only need to swap the runs‑on label to a Blacksmith runner — no major config rewrites.</li><li><strong>Better observability:</strong> You get deeper insights into pipeline performance and failures rather than the opaque logs you typically see in GitHub Actions.</li></ul><h4>Beyond the Cost-Per-Minute: The Hidden Factors</h4><p>Startups and smaller CI providers don’t have the decades of uptime and SRE experience that GitHub itself has built up. Public status trackers do show occasional service warnings or outages reported for services such as Blacksmith, just like any modern SaaS. <a href="https://github-incidents.pages.dev">And it’s not like that GitHub does not experience outages as well</a>.</p><p>That doesn’t mean these providers are flaky by default but it does mean you need to think about operational risk: how often does a CI provider go down? What’s your fallback? And how does that compare to simply accepting slower builds on a hyper‑scale provider?</p><p>For teams thinking about this, don’t ignore security and compliance either. CI runners execute arbitrary code and often have access to sensitive data, so the underlying platform needs to meet your security bar. In our case we were comfortable because the runner provider we chose publishes compliance details like SOC 2 Type II and standard data protection certifications, and runs jobs in isolated microVMs so that state isn’t shared across workflows.</p><h4>Conclusion</h4><p>Cutting down run times and CI costs means fewer context switches, faster PR cycles, and more predictable velocity. If you’re evaluating third‑party runners, there isn’t a single “right” choice — it’s a tradeoff between faster feedback, lower CI costs, and how much operational responsibility you want to shoulder for another service.</p><p>There’s a whole space of third-party runners out there (BuildJet, WarpBuild, Ubicloud, self-hosted options, etc.), and the right choice depends on your workload and priorities. But if you’re on GitHub Actions today and want an easy way to bump throughput without a big migration, Blacksmith or similar providers are worth trying.</p><h4>Appendix</h4><p>Just as I was about to post this article I had received an email from GitHub:</p><p><a href="https://github.blog/changelog/2025-12-16-coming-soon-simpler-pricing-and-a-better-experience-for-github-actions/">Coming soon: Simpler pricing and a better experience for GitHub Actions - GitHub Changelog</a></p><p>With this change, GitHub is introducing a $0.002 per-minute platform fee for all GitHub Actions usage, regardless of where jobs run. Going forward, CI costs will have two components:</p><ul><li>Compute costs (paid to whoever runs your runners, e.g. Blacksmith)</li><li>A GitHub platform fee, charged per minute of Actions usage</li></ul><p>If cost is the primary reason you are evaluating third party runner providers it may make sense to make a comparison. In our case, I expect the third party runner to still be cheaper overall.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=55612586afd7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Using Cursor’s Bugbot to Spot Issues Early in Pull Requests]]></title>
            <link>https://ali-dev.medium.com/using-cursor-bugbot-to-spot-issues-early-0cdc142fbaff?source=rss-3a534b5053e6------2</link>
            <guid isPermaLink="false">https://medium.com/p/0cdc142fbaff</guid>
            <category><![CDATA[cursor]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[github]]></category>
            <dc:creator><![CDATA[Ali Kamalizade]]></dc:creator>
            <pubDate>Tue, 16 Dec 2025 09:22:17 GMT</pubDate>
            <atom:updated>2026-01-18T21:40:09.347Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*x_VYL7qq7d0nj-bO" /><figcaption>Photo by <a href="https://unsplash.com/@cookiethepom?utm_source=medium&amp;utm_medium=referral">Cookie the Pom</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Most engineers know the familiar rhythm of a PR review: glance at the diff, hunt for logic mistakes, debate naming, rinse, repeat. It’s essential work, but it’s also incredibly easy to miss the small but critical bugs — off-by-one errors, edge case logic, subtle null handling problems — especially when PRs are large or rushed.</p><p>For teams like ours running a tight CI/CD pipeline (we use GitHub Actions), efficiency is everything. Every minute spent waiting for a human to spot a simple fix is a minute the code isn’t deployed and delivering value.</p><p>As we are already Cursor we naturally started using <strong>Bugbot from Cursor </strong>more and more as a first-pass reviewer on our pull requests: initially more out of curiosity as we had tried CodeRabbit in the past which we did not like. Bugbot is not replacing human reviewers, but it <em>changes when and where we catch issues</em>.</p><p>At a high level, Bugbot:</p><ul><li><strong>Runs automatically on new PRs and on updates</strong>, analyzing just the diff with context about intent and code relationships. It can also be triggered manually with commands like @cursor review or bugbot run.</li><li><strong>Comments directly on GitHub</strong> where it finds potential problems, complete with suggested fix ideas.</li><li>Supports <strong>custom rules via </strong><a href="https://cursor.com/en-US/docs/bugbot"><strong>.cursor/BUGBOT.md</strong></a> so you can encode team-specific guardrails or conventions.</li></ul><figure><img alt="Comment @cursor review or bugbot run to trigger another review on this PR" src="https://cdn-images-1.medium.com/max/1024/1*QldMM40fC_-V_Ns0GOOddQ.png" /></figure><h4>Benefits of using Bugbot in our development process</h4><p>Bugbot sits right at the start of that process, essentially becoming the fastest, least-tiring ‘first reviewer.’</p><ul><li><strong>Before CI:</strong> Bugbot scans the code on the PR.</li><li><strong>During CI:</strong> Our existing tests (like those run with Angular Testing Library and Testcontainers) catch functional regressions.</li><li><strong>After CI:</strong> Human reviewers focus on architectural decisions and complex logic.</li></ul><p>We’ve noticed a few concrete benefits from incorporating Bugbot into our flow:</p><ul><li><strong>Fewer “oh my bad” bugs late in review.</strong> When Bugbot flags a logic issue early, the human reviewer can focus on architecture and design trade-offs, not basic correctness.</li><li><strong>Better alignment on standards.</strong> Having auto-comments up front nudges authors toward patterns we care about <em>before</em> the first human comment lands.</li><li><strong>Faster iteration.</strong> If a suggestion can be fixed quickly (even automated via Cursor links), the turnaround on a clean, merge-ready PR improves.</li></ul><p>And if something specific keeps cropping up in your project (think domain-specific invariants or architectural patterns), we <em>can</em> add it as a Bugbot rule and let the bot enforce it consistently. That means fewer repetitive comments from reviewers and more time spent on what actually matters. A recent example: leave a comment on the PR to remind the engineer to bump the manifest version of our browser extension if the change should trigger a browser new extension release.</p><p>As with any AI assistant, there are edge cases. Bugbot doesn’t <em>always</em> catch every problem, and it may not understand deep business logic the way a domain expert reviewer does. It does not eliminate the need for a code review performed by an engineer. But using it as an early filter — catching obvious bugs and surfacing fix proposals — has already saved us plenty of time.</p><h4>Conclusion</h4><p>If your team is serious about minimizing noise and catching bugs earlier in the cycle, giving Bugbot rules a real place in your development workflow is worth exploring. Let me know about your experiences.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0cdc142fbaff" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Deployment ≠ Instant Worldwide Availability]]></title>
            <link>https://ali-dev.medium.com/deployment-instant-worldwide-availability-5cf00e5f290f?source=rss-3a534b5053e6------2</link>
            <guid isPermaLink="false">https://medium.com/p/5cf00e5f290f</guid>
            <category><![CDATA[continuous-delivery]]></category>
            <category><![CDATA[continuous-integration]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[api]]></category>
            <category><![CDATA[software-engineering]]></category>
            <dc:creator><![CDATA[Ali Kamalizade]]></dc:creator>
            <pubDate>Mon, 15 Dec 2025 09:52:16 GMT</pubDate>
            <atom:updated>2025-12-15T21:53:49.726Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*SUO5YgjoxFUClO8i" /><figcaption>Photo by <a href="https://unsplash.com/@cbpsc1?utm_source=medium&amp;utm_medium=referral">Clint Patterson</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Deployments succeed. The CI/CD pipeline greets you with a green checkmark. You celebrate. Then … a teammate reports they still see old behavior. “But the deployment succeeded!” you might think.</p><p>In this post, I want to share a quick reality check on distributed systems. Just because a deployment <strong>succeeded</strong> doesn’t mean it’s instantly <strong>available</strong> to all users. Depending on <em>what</em> you are deploying, the “time-to-live” can vary from milliseconds to literal days.</p><h4>What Actually Happens</h4><p>Here is a short overview of how this plays out in our stack (and this applies to lots of web-centric ecosystems as well):</p><p><strong>Backend: </strong>Once the service is live, API responses update right away. As soon as the new containers are healthy, traffic hits the new code.</p><p><strong>Web App: </strong>With the <em>next page refresh</em>, users see the update. Users who are currently active might still be running the old JavaScript bundle until they reload (or see a “New version available” gray toaster message).</p><p><strong>Office Add-In: </strong>Same mechanism as web apps if you change anything but the manifest, but with a twist. Users don’t “refresh” Outlook or Excel add-ins as often as browser tabs. They might keep the side panel open for days, running stale code.</p><p><strong>Extensions: </strong>Things get really interesting when we talk about browser extensions. We don’t control the delivery mechanism here — the browser stores do.</p><ul><li><strong>Chrome:</strong> It usually takes <strong>1–2 hours</strong> until it’s live in the store. After that, it can take <strong>up to a day</strong> for the Chrome browser to actually update the extension in the background.</li><li><strong>Edge:</strong> In many cases, the review process may take up to <strong>5 business days</strong>. Then, add another day for the background update to propagate to clients.</li></ul><h4>What This Means for You</h4><p>This latency creates a dangerous synchronization gap. If you ship a breaking API change thinking all your clients are updated, you’re potentially breaking the app for users somewhere between hours and a few days.</p><p>Deployment is what happens on your servers (which you control). Release is what happens on the user’s device (which you don’t).</p><blockquote>The trap is thinking that “deployment” and “release” are the same thing. They aren’t.</blockquote><p>If your deploy includes <strong>non-backwards-compatible changes</strong>, you need to assume:</p><ul><li>Some users will still be on the old client</li><li>Some will be on the new one</li><li>And some will flip over hours or <em>days</em> later</li></ul><h4>Best Practices</h4><p>Because of the lag in extensions and add-ins, your backend code effectively lives in the future while your users may still live in the past. To keep things running smooth, your API changes must be <strong>strictly backwards compatible</strong> for at least as long as your slowest delivery channel takes to update.</p><ul><li>Keep the backend compatible with old clients <em>until</em> you’re confident the majority of clients have updated.</li><li>Feature flags help you toggle behavior safely across disparate client versions.</li><li>Communicate expected propagation timelines to QA, support, and your teammates.</li><li>Watch real usage, not just CI success.</li></ul><h4>Conclusion</h4><p>A green CI/CD badge means your code made it out the door — not that everyone is instantly using it. In our ecosystem of web apps, add-ins, and extensions, <strong>real-world rollout is staggered and unpredictable</strong>:</p><ul><li>Web backends are available immediately</li><li>Web apps and add-ins update on refresh</li><li>Browser extensions take <em>hours to days</em> to land in users’ hands</li></ul><p>So, until the majority of clients are on the newest version, <strong>your backend and APIs still need to behave in a backwards-compatible way</strong>. Plan releases with this in mind — feature flags, compatibility guards, and clear communication between teams will save you time, confusion, and late-night fire drills.</p><p>In short: <strong>deploy ≠ delivered.</strong> Build as if some users will always be “stuck” on the version before the latest — because, for a while, they often are. Understanding rollout windows saves you from weird bugs and awkward chat threads at 2am.</p><p>How are your experiences?</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5cf00e5f290f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Sunhat raises €9.2 Million Series A to solve the “Proof Gap” for Enterprises]]></title>
            <link>https://ali-dev.medium.com/sunhat-raises-9-2-million-series-a-to-solve-the-proof-gap-for-enterprises-9a94610c3a11?source=rss-3a534b5053e6------2</link>
            <guid isPermaLink="false">https://medium.com/p/9a94610c3a11</guid>
            <category><![CDATA[sunhat]]></category>
            <category><![CDATA[funding]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[sustainability]]></category>
            <category><![CDATA[startup]]></category>
            <dc:creator><![CDATA[Ali Kamalizade]]></dc:creator>
            <pubDate>Tue, 23 Sep 2025 10:20:05 GMT</pubDate>
            <atom:updated>2025-09-24T08:44:35.385Z</atom:updated>
            <content:encoded><![CDATA[<h4>Led by CommerzVentures and joined by existing investors</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OCKaTRiTcr11k8wuDmpeuA.avif" /></figure><p>Thrilled to announce that <a href="https://www.getsunhat.com/?r=0">Sunhat</a> has raised €9.2M in Series A funding with the support of our newest partner, <a href="https://medium.com/u/5187f92870de">CommerzVentures</a>! 🚀</p><p>Since our last fundraising in 2024, a lot has happened but just to name a few highlights:</p><ul><li>🤖 Built Proof AI which is already used daily by customers in Europe, North America and the Middle East</li><li>🤝🏻 Grew the team of Sunnies by 2x</li><li>🇩🇪 Received Research Allowance on behalf of the German Federal Ministry of Education and Research</li><li>🔒 Achieved ISO 27001 certification</li></ul><h4>What’s next?</h4><p>The funding will help us take Proof AI to even greater heights:</p><ul><li>Expanding coverage beyond ESG to 100+ global standards and certifications</li><li>Connecting with even more enterprise systems and integration partners</li><li>Getting smarter with every questionnaire, audit, and assessment</li></ul><p>Reflecting on our journey so far, it is incredible to see what we have achieved so far. As the first person who has written code for Sunhat, it makes me proud to see how much the product is growing thanks to the stellar work of all Sunnies. In the last 18 months, I have been fortunate to be able to both grow myself in multiple areas (AI, security, management) and help others on their career as well.</p><p>A huge shoutout to my co-founders Lukas and Alex, the entire Sunhat crew and our investors for embarking on this awesome journey with us — the future continues to be exciting! Can’t wait to see what our team builds next 😎</p><p>Read more in our press release 🔽</p><p><a href="https://www.getsunhat.com/blog/sunhat-series-a">Sunhat raises €9.2 Million Series A to solve the &quot;Proof Gap&quot;</a></p><p>Thanks for your attention and take care! Until next time 👋</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9a94610c3a11" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How To Limit Lines + Show Tooltip If Text Is Too Long With CSS & JavaScript]]></title>
            <link>https://javascript.plainenglish.io/how-to-limit-lines-show-tooltip-if-text-is-too-long-with-css-javascript-e49c2e1804cb?source=rss-3a534b5053e6------2</link>
            <guid isPermaLink="false">https://medium.com/p/e49c2e1804cb</guid>
            <category><![CDATA[css]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[ux]]></category>
            <dc:creator><![CDATA[Ali Kamalizade]]></dc:creator>
            <pubDate>Tue, 19 Nov 2024 10:33:11 GMT</pubDate>
            <atom:updated>2024-12-13T21:15:27.375Z</atom:updated>
            <content:encoded><![CDATA[<h4>A little bit of CSS and an optional pinch of JavaScript to save the day.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*o_26fD8lKQFF-F1A" /><figcaption>Photo by <a href="https://unsplash.com/@morganharpernichols?utm_source=medium&amp;utm_medium=referral">Morgan Harper Nichols</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>There are lots of situations where user-defined text is displayed in web applications. If you only support one language, mobile-only and limit the amount of text to a minimum, it may be trivial to ensure a good layout. But the reality is often different: German text tend to be longer than English text, laptops have more space for displaying content and users can write entire poems in your software.</p><p>Using an ellipsis … is a commonly understood way to signal that there is more text than is being shown right now. To prevent an HTML element from overflowing and showing an ellipsis, one may write code like this:</p><pre>.withEllipsis {<br>  overflow: hidden;<br>  white-space: nowrap;<br>  text-overflow: ellipsis;<br>}</pre><p>This can work in many cases and we could end this post right here. But there are more advanced cases where this will not be enough:</p><ul><li>If you need to apply an ellipsis if text exceeds a specific amount of lines (e.g. after 3 lines)</li><li>If you need to apply custom logic if the text is being shortened (e.g. applying a tooltip)</li></ul><p>Maybe you are considering to use custom logic like <em>“if text is longer than 80 characters then apply ellipsis and shorten the text”</em> but this approach can also have quirks. Luckily, it does not have to be that complex.</p><h4>Introducing line-clamp</h4><p><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-line-clamp">-webkit-line-clamp</a> is a CSS property that will contain text to a given amount of lines when used in combination with display: -webkit-box or display: -webkit-inline-box. It will end with ellipsis when text-overflow: ellipsis is included. Browser support for -webkit-line-clamp is pretty good nowadays, covering all common modern browsers at this point.</p><figure><img alt="Good browser support for webkit-line-clamp" src="https://cdn-images-1.medium.com/max/1024/1*VbKSWeV1pV_1i5ifpZ0yEw.png" /></figure><p>To make the code reusable I created a <a href="https://sass-lang.com/documentation/at-rules/mixin/">SCSS mixin</a> but we can use vanilla CSS instead as well.</p><pre>@mixin limitLines($numberOfLines) {<br>  -webkit-line-clamp: $numberOfLines;<br>  overflow: hidden;<br>  display: -webkit-inline-box;<br>  -webkit-box-orient: vertical;<br>  text-overflow: ellipsis;<br>}</pre><p>This is already neat but we can go even further: what if we want to execute actions based on whether an ellipsis is shown? We can use a one-liner in JavaScript to determine whether we are seeing the ellipsis: which is the case if the text would be overflowing into more lines that we have specified using CSS. All we have to do is to compare scrollHeight (measurement of the height of an element’s content, including content not visible on the screen due to overflow) and clientHeight (inner height of an element in pixels, including padding but excluding borders, margins, and horizontal scrollbars).</p><pre>function isShowingOverflowEllipsis(htmlElement: HTMLElement): boolean {<br>  return htmlElement.scrollHeight &gt; htmlElement.clientHeight;<br>}</pre><p>In our scenario, scrollHeight will always be equal or bigger than clientHeight. Note: Depending on your use case, you can swap the order of arguments or compare scrollWidth and clientWidth instead.</p><h4>Example</h4><p>Let’s have a look at a real world example: I will be using Angular but the approach works with any other framework as well. Imagine we have an element which contains a user-defined message. This message could be up to 1000 characters long. To not give it too much space on a crowded page, we want to limit the message to a defined amount of lines. But users should still be able to see the full message if they want to do so by hovering the text.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OwaZl32_tjgoEGQUkVX1Uw.png" /></figure><p>In the screenshot above, we have applied a limit of 2 lines: if our text exceeds this, an ellipsis is shown automatically. Since the ellipsis is shown we also apply the tooltip.</p><figure><img alt="No ellipsis when text does not exceed maximum amount of lines" src="https://cdn-images-1.medium.com/max/1024/1*Qmm3yYmi-tcnW8GZ_8iAEA.png" /></figure><p>In this example, we have changed the maximum amount of lines to be 3. Since our text does not exceed 3 lines, it is displayed fully and with no ellipsis. Furthermore, no tooltip is applied as well because it would be unnecessary in this case: we can already see the full text.</p><h4>Conclusion</h4><p>Thanks for reading this short post. As you can see, a little bit of CSS and JS allows us to display multiline text and shorten it while having the option to react depending on this. This approach has worked well in our product. Let me know about your approaches. Till next time!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e49c2e1804cb" width="1" height="1" alt=""><hr><p><a href="https://javascript.plainenglish.io/how-to-limit-lines-show-tooltip-if-text-is-too-long-with-css-javascript-e49c2e1804cb">How To Limit Lines + Show Tooltip If Text Is Too Long With CSS &amp; JavaScript</a> was originally published in <a href="https://javascript.plainenglish.io">JavaScript in Plain English</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Reviewing My Past Infrastructure, Technology and Product Decisions]]></title>
            <link>https://ali-dev.medium.com/reviewing-my-past-infrastructure-technology-and-product-decisions-92f4853e5c1c?source=rss-3a534b5053e6------2</link>
            <guid isPermaLink="false">https://medium.com/p/92f4853e5c1c</guid>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[saas]]></category>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[software-engineering]]></category>
            <dc:creator><![CDATA[Ali Kamalizade]]></dc:creator>
            <pubDate>Wed, 06 Nov 2024 00:17:37 GMT</pubDate>
            <atom:updated>2025-03-18T21:42:50.402Z</atom:updated>
            <content:encoded><![CDATA[<h4>Your mileage may vary: here are some real life experiences</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*aHrZZEjl6Utsn5oF" /><figcaption>Photo by <a href="https://unsplash.com/@alexkixa?utm_source=medium&amp;utm_medium=referral">Alexandre Debiève</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>We are usually thinking about the future but it can be a good exercise to review decisions made in the past and check how well they held up. Inspired by this <a href="https://cep.dev/posts/every-infrastructure-decision-i-endorse-or-regret-after-4-years-running-infrastructure-at-a-startup/">post</a>, I want to use this post to review some product and technology choices which I pushed forward.</p><p>No matter how much we try to look at things objectively: choices are always biased to a certain degree. And it is not about bashing tools or vendors but about critically reviewing things that worked out for us and those that did not. That being said, let’s dive in!</p><p>The legend is as follows:</p><ul><li>✅ Recommend, I would likely do this again</li><li>⚠️ Works to a degree but with drawbacks and would consider alternatives</li><li>🛑 Would likely not attempt to do this again</li></ul><h4>✅ Linear</h4><p>Can Jira make sense in your organization? Sure. Is it overloaded and are there better alternatives? Definitely! <a href="https://linear.app/homepage">Linear</a> is not only great at what it does (helping us to plan and manage software projects effectively) but there is a lot to learn in <em>how</em> they do things (keyboard support, performance, UX design). Linear is worth the price point and we are very happy using it.</p><h4>(✅) Netlify</h4><p><a href="https://www.netlify.com">Netlify</a> is a great choice for hosting web projects. The Netlifolks created a simple yet solid platform to host any kind of web app. The developer experience is neat, especially when it comes to serverless.</p><p>The only downside for us is that this comes at a cost: Netlify is not as configurable or extensible if you need more power. And if you’re not 100% serverless then you will still need to use another cloud platform like AWS.</p><h4>✅ Cloudflare</h4><p><a href="https://www.cloudflare.com/developer-platform/products/">Cloudflare</a> does almost anything Netlify did for us yet it offers much more. I find the developer experience slightly worse than Netlify and I feel that configuration and the CLI could harmonize better. But at a similar price Cloudflare offers a lot of helpful goodies, especially around security such as a WAF (web application firewall) and AI (e.g. AI Workers). Overall, Cloudflare has been pretty reliable for us since setting things up.</p><h4>(✅) Render</h4><p>Do you like AWS but you want to reduce maintenance and setup costs? Then <a href="https://render.com">Render</a> might be a good choice for you. Similar to Netlify, it appeals strongly to startups and fast growing companies. With Render it’s simple to deploy and run a service: either by using the straightforward UI or by using infrastructure-as-code (which is what we do).</p><p>The main downside is that other tools may not provide a simple solution to connect with Render (AWS, GCP and Azure usually fare well in this regard). Of course Render does not offer all possible customization options that AWS would offer but the team is constantly shipping useful features that work. FWIW: Render is built on top of AWS.</p><h4>⚠️ Serverless Functions</h4><p>Personally, I am torn when it comes to serverless functions. As a user I like to have serverless as an option to only pay for what I consume. As a developer I like it when building a frontend that needs a backend part: serverless is handy for prototyping. But when it comes to developing and running those serverless functions in production on our own (e.g. with Cloudflare) I still prefer a “proper” backend service. While it may not always make sense for my own code I do like the serverless model in some products we use ourselves.</p><h4>✅ Angular</h4><p><a href="https://angular.dev">Angular</a> has evolved steadily over the last years. I feel it started to pick up significant pace since Angular 14 with the introduction of standalone components. Signals are another thing that make building reactive UIs easier as well. Of course, React’s ecosystem is still bigger but I rarely find this to be an issue in practice except rare cases. Angular is a framework and I like this fact.</p><h4>✅ Nest.js</h4><p>Overall, we are quite happy with <a href="https://nestjs.com">Nest.js</a>. If you like Angular you will Nest as well. Express.js is fine but I like the project structure and the testability that Nest brings out of the box. Similar to Angular, Nest is a framework and I like this fact.</p><h4>✅ Prettier</h4><p>It’s 2024, I do not want to deal with manually formatting code or even comment on PRs that things should be formatted in a certain way. <a href="https://prettier.io">Prettier</a> takes care of this.</p><h4>✅ Using a dedicated Log Management tool</h4><p>We are using <a href="https://betterstack.com/telemetry">BetterStack Telemetry</a> and it works pretty well for us. Log based alerts are a simple yet powerful way to deliver notifications over multiple channels to notify the team in case of issues. We have also created a bunch of presets for common scenarios such as to find endpoints with slow response times.</p><p>Their friendly and helpful support has been pretty stellar and deserves a special mention.</p><h4>✅ GitOps</h4><p>Git all the way. CI/CD or bust. Which brings me to…</p><h4>✅ GitHub Actions &amp; Workflows</h4><p>Since GitHub introduced <a href="https://github.com/features/actions">GitHub Actions</a>, GitHub finally won me over GitLab. Similar to npm packages, there is probably an action for whatever you want to do. And if there is not then it is often not that hard to create a custom workflow.</p><p>As someone who has created a bunch of workflows: while <a href="https://github.com/nektos/act">act</a> helps with local development and debugging workflows I wish GitHub would release more official tooling to simplify development.</p><h4>⚠️ Jest</h4><p>When <a href="https://jestjs.io">Jest</a> works it is great. There is a big ecosystem, the CLI is awesome and it works decently well with common frameworks. For me it was a big step up compared to Jasmine which I was used to before.</p><p>But if it does not then it can be quite annoying to deal with. I am not referring to missing web features unsupported by jsdom, I am talking about issues around TypeScript support and module imports causing test failures. While we are not actively looking to replace Jest at the moment I am keeping my eyes open for other test runners (e.g. vitest).</p><h4>✅ Testing Library</h4><p><a href="https://testing-library.com">Testing Library</a> is a lightweight solution to write UI component tests that rely on minimal implementation details and focus on user behavior instead. In our case, it is Angular Testing Library to be precise. In many cases writing tests with Testing Library has helped us uncovering usability issues like poor accessibility (e.g. a button with no text). There had been a few breaking changes when upgrading to the next major version but our overall experience has been pretty good.</p><h4>✅ Testcontainers</h4><p>You should write integration tests. And we find <a href="https://testcontainers.com">Testcontainers</a> to be a helpful to do so. Spinning up containers (e.g. a Postgres database) to test endpoints end-to-end becomes easy with Testcontainers.</p><h4>(✅) Nx</h4><p><a href="https://nx.dev">Nx</a> is a helpful tool to help us divide and manage our different product parts in a consistent way. The caching of task outputs is pretty nice as it can speed up local and especially CI workflow executions, saving CI minutes. That being said, updating Nx to the latest version has not always been trivial for us in the past and Nx can feel intimidating at first. Overall, it is still a strong net positive for us.</p><h4>✅ Just use Postgres</h4><p>Just use <a href="https://www.postgresql.org">Postgres</a> until you have a very good reason to use a database for special databases, e.g. a vector database. Postgres is open source and there is a large ecosystem around it.</p><h4>⚠️ MongoDB</h4><p>I like <a href="https://www.mongodb.com/atlas">MongoDB</a> in the early phases when all I need would be a database that takes anything and gives it back to me. MongoDB Atlas is a pretty compelling offering that I liked using in the past.</p><p>Nowadays, I rarely find a case where I would prefer MongoDB or other NoSQL databases over Postgres: SQL does have some benefits after all and jsonb can be used if we want to store arbitrary data as JSON. If you are not staying up-to-date with MongoDB you will have a bad time: a good friend of mine can tell you all about it.</p><h4>✅ Prisma</h4><p><a href="https://www.prisma.io">Prisma</a> is a fine ORM that plays well with TypeScript. We are generally quite happy with it. A considerable downside (and I guess this applies to other ORMs): by making it so easy to create sophisticated queries it naturally becomes easy to create pretty expensive SQL queries with Prisma.</p><h4>✅ Sentry</h4><p>Setting up <a href="https://sentry.io/welcome/">Sentry</a> is dead simple when getting started. The most important thing for us is the error monitoring and this is what we primarily use Sentry for. I am looking forward to seeing further improvements in performance monitoring.</p><h4>⚠️ Cloudinary</h4><p><a href="https://cloudinary.com">Cloudinary</a> is a solid product but it is not the best solution in all cases. If you are looking for a service handling all kinds of files (images, PDF, Excel, ZIP, …) you are better off looking at Amazon S3 or similar alternatives such as DigitalOcean Spaces or Cloudflare R2. If all you store are images and videos then Cloudinary is still a good tool with a nice admin dashboard.</p><h4>✅ DigitalOcean Spaces</h4><p><a href="https://www.digitalocean.com/products/spaces">DigitalOcean Spaces</a> is a simple to use solution for object storage. Their pricing is pretty fair and predictable. A huge advantage: its API is mostly compatible to Amazon S3 which means it is often compatible to tools that are compatible with S3.</p><h4>🛑 Clarity</h4><p>It’s free and easy to setup so I shouldn’t complain about <a href="https://clarity.microsoft.com">Clarity</a>. The video quality was shaky and inconsistent. Hence we dropped it. Maybe it works better on static websites.</p><h4>⚠️ Snyk</h4><p><a href="https://snyk.io">Snyk</a> is a solid product for analyzing code and dependencies. It hooks in nicely as a CI check in PRs and it can be invoked with their CLI as well. While I do not always agree with the way they rate vulnerabilities I do not find this to be a big issue in practice.</p><p>The thing that currently bothers me the most is a feature that is actually pretty neat: the creation of fix PRs that upgrade dependencies to a newer patched version. Unfortunately, despite my best efforts Snyk still occasionally creates PRs for dependencies that I explicitly excluded to be ignored (e.g. it should not create PRs for TypeScript as we’d typically upgrade TypeScript when upgrading Angular as well).</p><h4>✅ Sendgrid</h4><p><a href="https://sendgrid.com/en-us">Sendgrid</a> is a solid choice for sending emails. Note that even if you are not sending too many emails yet I would recommend to get a static IP address: without a static IP address your emails will more likely be blocked by corporate email filters.</p><h4>✅ Transifex</h4><p>Sooner or later, your will probably need to support more than one language. I toyed around with other translation management tools but I eventually settled with <a href="https://www.transifex.com">Transifex</a>. Collaboration works well and the GitHub integration is pretty neat: if it could only stop formatting files which creates larger PRs than necessary then it would be perfect.</p><h4>✅ SnapShooter</h4><p>When we think about backups most people probably think about backing up their relational database. But what about files uploaded by users — those are usually managed in a tool like S3. It took me a bit to set up a backup job in <a href="https://snapshooter.com">SnapShooter</a> (and one major f*ck up when I accidentally deleted all files— luckily, I had previously downloaded all files locally hence I could restore before customers noticed anything; this is a tale for another time) but the daily backup job is now working reliably and I feel more at ease.</p><h4>✅ Metro Retro</h4><p>If you are doing retrospectives (and you should do it from time to time) then I can recommend <a href="https://metroretro.io">Metro Retro</a>. Not only do I like the name, it also provides the essentials for conducting a retro including nice templates and the ability to throw confetti. Figjam or Miro are also decent alternatives.</p><p>These tools help but in the end, it is a team effort to conduct retros that help the team.</p><h4>✅ Font Awesome Pro</h4><p>The free version of <a href="https://fontawesome.com/icons">Font Awesome</a> is already pretty nice and the pro version builds up on the strong foundation, providing an icon for almost every case you will ever need.</p><p>One thing that slightly bothers me as a user is the private registry bandwidth: we are usually exceeding it since we have multiple GitHub workflows that call npm ci and we push/deploy a lot. One way to solve would be to store the Font Awesome files in the repository instead of installing them through npm: hence I will not hold it against them.</p><h4>✅ Linters</h4><p><a href="https://stylelint.io">Stylelint</a> and <a href="https://eslint.org">ESLint</a> are two helpful tools to get actionable feedback in the IDE and on CI. I am a big believer of automating the things that can be automated: everything which can be enforced automatically and which removes guess work or need for manual documentation should probably be automated.</p><p>Some Stylelint rules which we found to be helpful are around enforcing consistency (e.g. keep variable names at the top of stylesheets) and common issues (e.g. prevent using certain selectors). When it comes to ESLint I find framework specific rules (e.g. <a href="https://github.com/angular-eslint/angular-eslint">angular-eslint</a>, <a href="https://github.com/darraghoriordan/eslint-plugin-nestjs-typed">eslint-plugin-nestjs-typed</a>) to be particularly useful, especially for engineers who may not be familiar with certain frameworks yet.</p><h4>✅ GitGuardian</h4><p>Secrets should be, well, secret. And they should also not be hardcoded. <a href="https://www.gitguardian.com">GitGuardian</a> provides capabilities to fix hardcoded secrets in Git repositories. It hooks in nicely as a CI check in PRs and I get alerted in case it finds secrets in our source code.</p><h4>⚠️ Pendo</h4><p><a href="https://www.pendo.io">Pendo</a> is a good product analytics tool and it is not too difficult to get started with it. However, it is fairly expensive and I intend to review other alternatives that may cover areas as well such as feature flagging. I have heard good things about <a href="https://posthog.com">Posthog</a>.</p><h4>(✅) Leveling up your compliance and security efforts early</h4><p>Does a startup in year 1/2/3 need to be compliant to frameworks like ISO 27001? Not necessarily but you probably know how things tend to go: the earlier you set up the right foundations, the less effort you need to make in the future when your organization has grown slower and less nimble.</p><p>We are using <a href="https://drata.com">Drata</a> which automates compliance and best practices around security in our company. It also helps us maintain our ISMS (information security management system). There are some tools that are similarly capable like Vanta and Secureframe but I am happy with Drata. The integrated guidance helped me quite a lot not only in setting up processes and policies but understanding why things are the way they should be.</p><h4>🛑 Not introducing a MDM earlier</h4><p>MDM (mobile device management) helps us manage and secure our devices. One useful feature is the ability to install software (e.g. VS Code) directly through the MDM, reducing the onboarding efforts for new colleagues. If you wanna pursue security certifications such as ISO 27001 or SOC 2, a MDM can help with getting your organization ready.</p><p>Depending on the OS there are a couple of good tools that cost fairly similarly (e.g. Jamf, Swif, Jumpcloud, Kandji). We are using <a href="https://www.swif.ai">Swif</a> which works well for us.</p><h4>Conclusion</h4><p>Thanks for reading this post! It was interesting to look back and see how some things turned out better than expected and how there are still some things on my todo list. Let me know in the comments about your experiences.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=92f4853e5c1c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Preserve Line Breaks of Textarea Values with CSS]]></title>
            <link>https://medium.com/codex/how-to-preserve-line-breaks-of-textarea-values-with-css-89a6e6e45b39?source=rss-3a534b5053e6------2</link>
            <guid isPermaLink="false">https://medium.com/p/89a6e6e45b39</guid>
            <category><![CDATA[html]]></category>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[css]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Ali Kamalizade]]></dc:creator>
            <pubDate>Sat, 31 Aug 2024 11:57:29 GMT</pubDate>
            <atom:updated>2024-08-31T11:57:29.549Z</atom:updated>
            <content:encoded><![CDATA[<h4>CSS is all you need</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Zexv-68AYQHAFuLJ" /><figcaption>Photo by <a href="https://unsplash.com/@sandym10?utm_source=medium&amp;utm_medium=referral">Sandy Millar</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>The <strong>textarea</strong> element is a useful native HTML element for allowing users to write multiline text. Unlike a regular <strong>input</strong> element, textarea can contain line breaks.</p><p>A common use case is to render the content of a textarea as plain text. Frameworks such as Angular, React and Vue.js make it easy to render text based on a property. E.g. with Angular you can do something like this:</p><pre>&lt;textarea *ngIf=&quot;!readonly&quot; [(ngModel)]=&quot;description&quot;&gt;&lt;/textarea&gt;<br>&lt;div *ngIf=&quot;readonly&quot;&gt;{{ description }}&lt;/div&gt;</pre><p>A potentially surprising issue is that line breaks may not be rendered as expected. See the example below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0MU7jXUFq6InPBGgTXLnEg.png" /></figure><p>As you can tell, line breaks are ignored and text is rendered as one big block even though the textarea itself looks fine. Line breaks in textarea elements are typically stored as\n. And frameworks like Angular sanitize content rendered via data binding which is a good thing for various reasons like security.</p><p>Let’s investigate this issue and see how we can solve this with ease.</p><h4>CSS to the rescue</h4><p>A native approach could be to insert HTML. It is simple to use the innerHTML property or one of the DOM APIs to insert the text programmatically. However, we should avoid inserting raw HTML if possible as that can introduce the possibility of injection and it feels like shooting birds with cannons.</p><p>Luckily, there’s a simple <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/white-space#pre-line">CSS property</a> called white-space we can use to solve our little issue. This property can take a couple of different values but I found pre-line to be what I’d typically need.</p><pre>/* preserve line breaks for text originating from a textarea */<br>.preserveTextareaLineBreaks {<br>  white-space: pre-line;<br>}</pre><p>Now let’s have another look at our simple example after applying our white-space change:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BvSBFz9_5GUgTB7_g12UCQ.png" /></figure><p>As you can tell, we can now see line breaks in the rendered text thanks to our little CSS change. If this is something we need more than a few times it may make sense to create a global CSS class that can be referenced across our application.</p><h4>Conclusion</h4><p>Thanks for reading this short post! Simple problem, simple solution — is that not what we want? Let me know in the comments about alternative approaches you have taken.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=89a6e6e45b39" width="1" height="1" alt=""><hr><p><a href="https://medium.com/codex/how-to-preserve-line-breaks-of-textarea-values-with-css-89a6e6e45b39">How to Preserve Line Breaks of Textarea Values with CSS</a> was originally published in <a href="https://medium.com/codex">CodeX</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>