<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ilyas Elaissi</title>
    <description>The latest articles on DEV Community by ilyas Elaissi (@ilyas_elaissi).</description>
    <link>https://dev.to/ilyas_elaissi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3952383%2F9e65fd9b-3c27-4e7a-bbb1-dc009de2a46d.jpg</url>
      <title>DEV Community: ilyas Elaissi</title>
      <link>https://dev.to/ilyas_elaissi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ilyas_elaissi"/>
    <language>en</language>
    <item>
      <title>The Best CI Server Tools: Honest Picks for Every Dev Team</title>
      <dc:creator>ilyas Elaissi</dc:creator>
      <pubDate>Mon, 15 Jun 2026 18:45:16 +0000</pubDate>
      <link>https://dev.to/ilyas_elaissi/the-best-ci-server-tools-honest-picks-for-every-dev-team-m0j</link>
      <guid>https://dev.to/ilyas_elaissi/the-best-ci-server-tools-honest-picks-for-every-dev-team-m0j</guid>
      <description>&lt;p&gt;Picking the wrong CI Server Tools early in a project costs more than people expect not just in migration time, but in the slow friction of a pipeline that fights your team instead of helping it. I have watched teams rebuild their entire automation setup six months in because they chose something that felt familiar rather than something that fit. This guide is built around what actually matters when you are comparing options: realistic trade-offs, honest limitations, and which tool fits which situation.&lt;/p&gt;




&lt;h2&gt;
  
  
  What a CI/CD Pipeline Actually Does
&lt;/h2&gt;

&lt;p&gt;Most "what is CI/CD pipeline in DevOps" explanations bury the practical point. Here is the short version: every time a developer pushes commits to a shared repository, the system automatically kicks off a build, runs tests, applies code quality gates, and (if you have configured continuous delivery) pushes artifacts toward production. That automated build process removes the "works on my machine" problem by running everything in a clean, consistent environment.&lt;/p&gt;

&lt;p&gt;The continuous integration workflow is not just about catching bugs. It is about shrinking the feedback loop. A build that takes 40 minutes means a developer has already switched context twice before they see a failure. Most teams targeting healthy delivery speed aim for pipelines under 10 minutes for the critical path, using parallel test execution to get there.&lt;/p&gt;

&lt;p&gt;Deployment automation at the end of the pipeline handles the CD side — staging promotions, environment-specific configs, rollback triggers. The pipeline setup is where most teams spend the most time, and it is also where the choice of tooling matters most.&lt;/p&gt;




&lt;h2&gt;
  
  
  The CI Server Tools Landscape Right Now
&lt;/h2&gt;

&lt;p&gt;👉 Also read: &lt;a href="https://codetips.dev/ci-cd-tools-for-devops" rel="noopener noreferrer"&gt;Complete CI/CD Tools Guide for DevOps Teams&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A useful CI/CD tools list for DevOps teams spans at least three categories: self-hosted open source, cloud-native SaaS, and enterprise platforms. Each has a different total cost of ownership profile, and conflating them is how teams end up paying for the wrong thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open source, self-hosted:&lt;/strong&gt; Jenkins, GitLab CI (self-managed), and a few smaller projects like Woodpecker CI. Free to license, but you pay in infrastructure and maintenance time. Running a self-hosted CI server means your team owns patching, storage, and uptime. For teams with strict data residency requirements or air-gapped environments, this is often non-negotiable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cloud-native SaaS:&lt;/strong&gt; GitHub Actions, CircleCI, Travis CI, Bitbucket Pipelines. These run on someone else's infrastructure. The pipeline setup is faster because you skip server provisioning entirely. The cost model shifts to compute minutes, which can surprise you at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enterprise platforms:&lt;/strong&gt; Azure DevOps, TeamCity, Bamboo, Harness. Built with enterprise CI readiness in mind — role-based access, audit logs, compliance hooks, dedicated support. The price reflects it. For organizations running hundreds of pipelines across dozens of teams, the governance features often justify the spend.&lt;/p&gt;

&lt;p&gt;The open source vs commercial trade-off is real: open source tools give you flexibility and zero licensing cost, but someone on your team has to own them. Commercial tools give you reliability and support, but vendor lock-in is a genuine risk, especially when your pipeline definitions become deeply coupled to a proprietary YAML dialect.&lt;/p&gt;




&lt;h2&gt;
  
  
  Jenkins: Still Standing, but at a Cost
&lt;/h2&gt;

&lt;p&gt;Jenkins is the most widely deployed CI server in the world by install count. Its plugin ecosystem is vast — over 1,800 plugins cover everything from Slack notifications for build failure alerts to Kubernetes-native executor scaling. That breadth is also its problem.&lt;/p&gt;

&lt;p&gt;Every significant Jenkins setup I have seen eventually develops a plugin dependency hell situation. You update the Git plugin, it pulls a new version of a shared library, and two unrelated jobs start failing. The core is stable, but the ecosystem is not well-coordinated. Teams that run Jenkins well tend to pin plugin versions aggressively and treat upgrades like production deployments.&lt;/p&gt;

&lt;p&gt;The Jenkinsfile (declarative pipeline syntax) is reasonably clean, and version control integration for pipeline definitions is mature. But the UI is showing its age, and initial pipeline configuration for new developers is not friendly. Jenkins is a strong choice if you need maximum flexibility, have infrastructure experience on the team, and are running in an environment where SaaS is not viable. It is a rough choice for a team that just wants builds to work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simple Jenkinsfile declarative pipeline example&lt;/span&gt;
&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;
    &lt;span class="n"&gt;triggers&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;pollSCM&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'H/5 * * * *'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;stages&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Build'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'mvn clean package -DskipTests'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Test'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'mvn test'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Deploy to Staging'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;branch&lt;/span&gt; &lt;span class="s1"&gt;'main'&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'./deploy.sh staging'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;failure&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;mail&lt;/span&gt; &lt;span class="nl"&gt;to:&lt;/span&gt; &lt;span class="s1"&gt;'team@example.com'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;subject:&lt;/span&gt; &lt;span class="s1"&gt;'Build Failed'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="s2"&gt;"Check ${env.BUILD_URL}"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  GitHub Actions and GitLab CI: The Repo-Native Options
&lt;/h2&gt;

&lt;p&gt;These two have eaten a significant share of the market because they removed the setup barrier almost entirely. Your pipeline lives in the same repository as your code — a &lt;code&gt;.github/workflows&lt;/code&gt; directory for Actions, a &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; file for GitLab CI. No separate server to configure, no webhook setup, no credentials wrangling between two different systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Actions&lt;/strong&gt; is the right default for teams already on GitHub. The triggers system is genuinely flexible: you can fire workflows on push, pull request, schedule, repository dispatch, or manual approval. The marketplace has thousands of prebuilt actions that handle common tasks without custom scripting. The free tier offers 2,000 minutes per month on public repos and 500 MB of storage, which covers most small teams. Costs scale linearly beyond that based on compute minutes and the runner type (Linux, Windows, or macOS, each priced differently).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitLab CI&lt;/strong&gt; is arguably more opinionated and more complete. It ships as part of GitLab's integrated DevOps platform, so shared repository management, merge request controls, package registries, and security scanning all plug into the same system. The YAML syntax is stricter than Actions but easier to debug. GitLab's self-managed option also makes it the strongest open source CI CD tools for self-hosting if you want a full platform rather than just a build runner. The main limitation: if your code is on GitHub or Bitbucket, GitLab CI creates friction, not less.&lt;/p&gt;

&lt;p&gt;One honest caveat on both: large monorepos with hundreds of services can hit compute cost ceilings fast. Parallel test execution helps, but you are paying per minute, and those minutes add up. I have seen mid-sized teams blow past $500/month on Actions without realizing it because no one set spending limits.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Commercial Middle Ground
&lt;/h2&gt;

&lt;p&gt;👉 Also read: &lt;a href="https://codetips.dev/devops-pipeline" rel="noopener noreferrer"&gt;How to Build a Solid DevOps Pipeline&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CircleCI&lt;/strong&gt; sits between "dead simple" and "fully flexible." Its orb system (reusable pipeline config packages) is the clearest implementation of what good pipeline modularity looks like. Setup is fast, the parallelism configuration is explicit, and the resource class system lets you right-size compute per job. The free plan is generous for individuals. Teams at growth-stage companies often land here because it scales better than Actions on complex pipelines without requiring self-hosting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Azure DevOps&lt;/strong&gt; is worth taking seriously if your organization runs on Microsoft infrastructure. The integration with Azure services is tight, and AWS CI/CD tools for cloud deployments have AWS CodePipeline as a native alternative, but Azure Pipelines is more mature as a standalone CI/CD system. The YAML pipeline format is verbose but well-documented. Licensing costs are tied to parallel jobs and user count — budget carefully.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TeamCity&lt;/strong&gt; from JetBrains has one of the best UIs in the category. Configuration as code is supported, build chains between projects are first-class, and the free tier allows up to 100 build configurations and 3 build agents. Teams that use IntelliJ-based IDEs often find the toolchain integration unusually smooth. The downside: the server is Java-based and memory-hungry. A production TeamCity instance wants at least 4 GB of heap, and scaling agents requires infrastructure attention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bamboo&lt;/strong&gt; (Atlassian) and &lt;strong&gt;Bitbucket Pipelines&lt;/strong&gt; make sense together if you are already in the Atlassian ecosystem — Jira, Confluence, Bitbucket. Bamboo is self-hosted and integrates tightly with Jira deployments tracking. Bitbucket Pipelines is the cloud version, simpler and less powerful. Neither is a compelling choice outside the Atlassian stack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Travis CI&lt;/strong&gt; was once the default for open source projects. Its pricing changes and reliability issues over the past few years pushed most of that community toward GitHub Actions. It still works, but it is not where I would start a new project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Harness&lt;/strong&gt; is in a different category than the others. It is primarily a continuous delivery platform, not just a CI runner, with built-in canary deployments, feature flags, cloud cost optimization, and ML-driven rollback triggers. The pricing is enterprise-grade. For teams at the scale where deployment automation complexity justifies a dedicated platform, it is worth evaluating. For teams shipping one app to one environment, it is overkill.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Choose the Right CI Server Tool for Your Team
&lt;/h2&gt;

&lt;p&gt;The most used CI/CD tools in DevOps are not necessarily the right ones for your context. Here is a practical way to cut through the noise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start with where your code lives.&lt;/strong&gt; If it is on GitHub, GitHub Actions is the path of least resistance and almost always the right starting point. If it is on GitLab, GitLab CI is built in. Forcing a third-party tool onto a repo-native workflow creates extra credential management and webhook maintenance with limited upside.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Estimate your build volume honestly.&lt;/strong&gt; Run a rough calculation: average build duration in minutes times daily builds times team size. If that number is small, the free tier of most SaaS tools covers you. If it is large, the per-minute cost model of cloud CI will matter, and self-hosting becomes economically competitive even after accounting for engineering time to maintain it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think about the DevOps toolchain integration you need.&lt;/strong&gt; A team deploying to Kubernetes probably wants native container build support and RBAC on deployment jobs. A team shipping a mobile app needs macOS runners, which are expensive on every platform. A regulated industry team needs audit logs and approval gates at the pipeline level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not optimize for plugins.&lt;/strong&gt; This is where teams get seduced by Jenkins. The plugin count looks like capability. In practice, the plugins you actually use number fewer than 20, and most modern SaaS tools cover those natively. The plugin advantage matters at the edges, not in the common case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The best free CI/CD tools for small teams&lt;/strong&gt; are GitHub Actions (if you are on GitHub) and GitLab CI (if you are on GitLab or willing to self-host). Both are genuinely capable at zero cost for low-to-medium build volumes. CircleCI's free tier is a reasonable third option. For reference, the &lt;a href="https://cloud.google.com/devops/state-of-devops/" rel="noopener noreferrer"&gt;State of DevOps Report&lt;/a&gt; consistently shows that elite-performing teams invest in build infrastructure but are not necessarily spending more — they are spending more thoughtfully.&lt;/p&gt;

&lt;p&gt;A tool switch mid-project is painful. The pipeline configurations, secrets management, caching strategies, and team habits all have to move together. Choosing deliberately the first time saves roughly two to three weeks of migration work later.&lt;/p&gt;




&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is CI/CD tools in plain terms?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;CI/CD tools are the software that watches your code repository for changes and automatically builds, tests, and deploys your application when those changes arrive. The "CI" side runs tests and quality checks on every commit. The "CD" side pushes passing builds toward production, either automatically or with a manual approval step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which open source CI CD tools are best for self-hosting?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Jenkins and GitLab CI (self-managed) are the two most mature options. Jenkins gives you more flexibility through its plugin system; GitLab CI gives you a more complete platform (code hosting, pipelines, registries) in one install. If your team lacks dedicated infra experience, GitLab CI's integrated approach is usually easier to operate at a reasonable scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I choose the right CI server tool when I am just starting out?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Start with whatever is native to your code host. GitHub Actions if you are on GitHub, GitLab CI if you are on GitLab. Do not overthink it. You can always migrate once you have a clearer picture of your actual build requirements — but you probably will not need to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are AWS CI/CD tools a good fit for cloud deployments?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AWS CodePipeline and CodeBuild are functional, but they feel narrow outside the AWS ecosystem. If your entire stack lives in AWS and you want tight IAM integration without managing credentials externally, they are worth considering. But honestly, most teams running on AWS still use GitHub Actions or CircleCI and just grant those tools scoped IAM roles. The native AWS tooling is not compelling enough to replace a better-designed CI system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes a CI pipeline "enterprise ready"?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mostly not the features people think of first. The basics matter everywhere. What tips a tool into enterprise territory is: fine-grained RBAC so not everyone can push to production, audit logs that satisfy compliance reviews, high availability for the CI server itself, and SLA-backed support. TeamCity, Azure DevOps, and Harness check those boxes. Jenkins can too, but it requires significant custom configuration to get there.&lt;/p&gt;

</description>
      <category>ci</category>
      <category>cicd</category>
      <category>devops</category>
    </item>
    <item>
      <title>What is UI Design? The Honest Beginner's Guide to How It Works</title>
      <dc:creator>ilyas Elaissi</dc:creator>
      <pubDate>Fri, 12 Jun 2026 20:29:36 +0000</pubDate>
      <link>https://dev.to/ilyas_elaissi/what-is-ui-design-the-honest-beginners-guide-to-how-it-works-opg</link>
      <guid>https://dev.to/ilyas_elaissi/what-is-ui-design-the-honest-beginners-guide-to-how-it-works-opg</guid>
      <description>&lt;p&gt;Most people who use apps and websites every day have no idea how much thought goes into a single button. The color, the size, the spacing, the way it responds when you tap it — that is UI design. Understanding what is UI design and how does it work is the first step toward either appreciating the craft or building a career in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Idea: What Is UI Design?
&lt;/h2&gt;

&lt;p&gt;UI design is the practice of crafting the visual elements of a digital product that users actually touch, tap, and click. Every screen you interact with on your phone or computer was designed by someone. They chose the typography, picked the color palette, arranged the layouts, drew the icons, and decided how everything fits together.&lt;/p&gt;

&lt;p&gt;The "UI" stands for user interface, and interfaces come in several forms. A graphical user interface (GUI) is the most familiar — it is the screen-based visual layer on your operating system, your apps, your browser. But there are also voice interfaces, gesture-based interfaces, and even hardware control panels. When most people say "UI design," they mean the visual, screen-based kind.&lt;/p&gt;

&lt;p&gt;The job is not purely aesthetic. A beautiful interface that confuses users has failed. The goal is to create something that looks good &lt;em&gt;and&lt;/em&gt; behaves predictably. Visual hierarchy, the principle of guiding a user's eye toward what matters most, is one of the core tools for doing that. If someone lands on a page and cannot figure out where to click first, the visual hierarchy is broken — regardless of how polished the colors look.&lt;/p&gt;




&lt;h2&gt;
  
  
  UI vs. UX: The Distinction That Trips Everyone Up
&lt;/h2&gt;

&lt;p&gt;People mix these up constantly, including some job postings. Here is the clearest way to think about the difference between UI and UX with an example: imagine you are ordering food at a restaurant. The UX is the whole experience — the wait time, the menu clarity, whether your order came out right. The UI is what the menu looks like. Both matter. They affect each other. But they are different jobs.&lt;/p&gt;

&lt;p&gt;UX (user experience) design deals with the overall user journey: what path does a person take to complete a task, where do they get confused, what do they need at each stage. It involves information architecture (how content is organized and labeled), user research, and interaction design (how the product responds to what users do).&lt;/p&gt;

&lt;p&gt;UI design sits on top of that foundation. Once a UX designer has mapped out the flow and structure, the UI designer makes it visually coherent and appealing. Typography choices, spacing, color contrast, component states — that is the UI layer.&lt;/p&gt;

&lt;p&gt;Some designers do both, which is why "UI/UX designer" is a common job title. But the skills are distinct, and larger teams usually separate the two roles.&lt;/p&gt;




&lt;h2&gt;
  
  
  What a UI Designer Actually Does Day to Day
&lt;/h2&gt;

&lt;p&gt;A common question worth addressing directly: what does a UI designer do every day?&lt;/p&gt;

&lt;p&gt;The honest answer is that it varies a lot by team size and stage of product. At a startup, one designer might handle everything from early sketches to final pixel-perfect specs. At a larger company, the work is more specialized. But broadly, here is what the day-to-day looks like.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Visual design work.&lt;/strong&gt; Most of the time is spent in a design tool — Figma is the dominant one right now, though Adobe XD and Sketch are still used on some teams. You are building components, refining layouts, adjusting spacing, choosing typefaces, and making sure everything looks consistent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maintaining a design system.&lt;/strong&gt; Most mature products have a design system: a documented set of reusable components, color tokens, spacing rules, and design principles that keep the product consistent. Keeping that system up to date is ongoing work. The component library inside that system is what lets teams ship new features without reinventing the button every time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Collaborating with developers and UX designers.&lt;/strong&gt; UI designers spend real time in meetings, Slack threads, and review sessions. They hand off specs to developers, answer questions about edge cases, and push back when an implementation drifts from the design. Tools like InVision were built partly to close this handoff gap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reviewing and iterating.&lt;/strong&gt; Designs rarely ship unchanged. Feedback comes from stakeholders, developers, and sometimes usability testing sessions where real users try the product. Iteration is not a sign that the first version was bad — it is just how the process works.&lt;/p&gt;

&lt;p&gt;The roles and responsibilities of a UI designer also touch accessibility. Making sure color contrast ratios meet WCAG standards, that font sizes are readable, that interactive elements are reachable by keyboard — accessibility in design is part of the job, not a bonus feature.&lt;/p&gt;




&lt;h2&gt;
  
  
  How the Design Process Works in Practice
&lt;/h2&gt;

&lt;p&gt;The UI design process does not start with colors and fonts. It starts with understanding.&lt;/p&gt;

&lt;p&gt;Before any visual work begins, a designer (or the team) researches the target users, reviews any existing product, and gathers requirements. User-centered design is not a buzzword; it is a practical constraint. If you do not know who is using the product, you cannot make good decisions about type size, information density, or navigation patterns.&lt;/p&gt;

&lt;p&gt;From research, the work moves into wireframes. These are low-fidelity sketches, digital or on paper, that map out structure without any visual polish. The point is to sort out information architecture and user flow before investing time in visual design.&lt;/p&gt;

&lt;p&gt;Then come prototypes — interactive mockups that simulate how the product will work. Prototypes let teams test the design thinking behind a product before any code is written. This is also when usability testing tends to happen. Watching five real users try to complete a task in a prototype will surface problems faster than any internal review.&lt;/p&gt;

&lt;p&gt;Only after that foundation is solid does the UI designer focus on visual execution: color, typography, icons, spacing, animation, and all the details that turn a wireframe into something that feels finished.&lt;/p&gt;

&lt;p&gt;That sequence matters. Skipping straight to visual design before the structure is right is one of the most common reasons products end up needing expensive redesigns later.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real Examples: Good Interfaces and Bad Ones
&lt;/h2&gt;

&lt;p&gt;UI/UX design examples are everywhere, and the contrast between good and bad is sharper than most people expect.&lt;/p&gt;

&lt;p&gt;Google's search results page is a classic example of strong UI. Almost no visual noise. The hierarchy is clear: query at the top, results below, ads visually separated. You never wonder what to do next.&lt;/p&gt;

&lt;p&gt;Bad user interface design examples are also easy to find. The classic Microsoft Word ribbon (pre-2019) crammed hundreds of functions into a toolbar that required training to understand. Early e-commerce checkout flows that buried the "continue" button or forced account creation before purchase had measurable abandonment rates as a direct result. Dark patterns — interfaces deliberately designed to confuse users into unwanted actions — are also a form of bad UI design, one that is increasingly drawing regulatory attention.&lt;/p&gt;

&lt;p&gt;Material Design, Google's open design system, is a useful reference for what principled UI looks like. It documents how to handle elevation, color, motion, and typography in a consistent, accessible way. Worth reading even if you never build with it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building a Career in UI Design
&lt;/h2&gt;

&lt;p&gt;The career case for UI design is real, though it is worth being honest about what the market actually looks like.&lt;/p&gt;

&lt;p&gt;Demand is genuine. Companies that used to rely on engineers to handle visual decisions have learned that the product suffers for it. Dedicated UI designers are now expected on any product team of meaningful size.&lt;/p&gt;

&lt;p&gt;Pay reflects that demand. A UI/UX designer salary in the US typically falls between $70,000 and $120,000 depending on experience, location, and whether the role is more senior or more specialized. Senior product designers at larger tech companies can earn considerably more. Entry-level roles are tighter, and competition is real.&lt;/p&gt;

&lt;p&gt;The scope of UI design career opportunities has also widened because of AI. AI-driven development tools have made it easier for non-developers to build apps and websites, which means more products that need thoughtful visual design. That is not a threat to designers — it is more clients, more products, more demand.&lt;/p&gt;

&lt;p&gt;The field also connects to broader digital product design: mobile apps, web apps, desktop software, kiosks, in-car displays. Every screen is a potential surface for a UI designer.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Learn UI Design: Courses vs. Self-Study
&lt;/h2&gt;

&lt;p&gt;There is a lot of free material available. YouTube channels, articles, and open-source design resources can take someone surprisingly far. For someone who just wants to understand the basics or build a small personal project, self-directed learning works fine.&lt;/p&gt;

&lt;p&gt;But if you are trying to break into the industry professionally, the honest advice is: get a credential that signals you know what you are doing. Portfolios matter more than degrees in this field, but a structured program teaches you the vocabulary, the process, and the professional habits that are hard to pick up from scattered tutorials.&lt;/p&gt;

&lt;p&gt;A good UI design course online should cover typography, color theory, wireframing, prototyping, design systems, accessibility, and at least one industry-standard tool (Figma, most likely). The Interaction Design Foundation offers solid foundational courses at low cost. Coursera has programs from recognized universities. The UX Design Institute offers a professional certification that covers UI design comprehensively, including typography, iconography, design principles, and tools — worth considering if you want something structured and recognized by employers.&lt;/p&gt;

&lt;p&gt;Whatever path you take, build a portfolio while you learn. Redesign an app you find frustrating. Create a fictional product from scratch. The portfolio is what gets you hired, and you need it before you need the certificate.&lt;/p&gt;

&lt;p&gt;👉 Also read: &lt;a href="https://codetips.dev/complete-web-design-workflow-for-2026" rel="noopener noreferrer"&gt;Complete Web Design Workflow for 2026&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is UI design and how does it work?&lt;/strong&gt;&lt;br&gt;
UI design is the process of creating the visual layer of a digital product — the screens, buttons, typography, icons, and layouts that users interact with. It works by translating a product's structure and functionality (usually defined by UX design) into a visually coherent, accessible, and appealing interface. Designers use tools like Figma to build and iterate on these visuals before handing them to developers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is UI/UX, and are they the same thing?&lt;/strong&gt;&lt;br&gt;
Not exactly. UI covers the visual side of a product: how it looks. UX covers the experience side: how it works, how it flows, and how it feels to use over time. They overlap significantly, and many designers work across both. But treating them as identical leads to products where everything looks polished but the flow is confusing — or vice versa.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How long does it take to learn UI design?&lt;/strong&gt;&lt;br&gt;
Honestly, you can pick up the basics in a few weeks. Getting good enough to land a junior role typically takes 6 to 12 months of focused practice, especially if you are building a portfolio alongside studying. It depends on how many hours you put in and whether you get real feedback on your work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are there any free resources for learning UI design?&lt;/strong&gt;&lt;br&gt;
Yes. The Interaction Design Foundation has a free tier. YouTube has solid foundational content. Figma itself has a free plan that is fully functional for learning. The trap is spending too long in tutorial mode without building anything. At some point, the most valuable thing you can do is design something, share it, and get it criticized.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What separates a good UI from a bad one?&lt;/strong&gt;&lt;br&gt;
A good UI gets out of the way. Users can complete their task without thinking about the interface. A bad UI creates friction: confusing labels, inconsistent patterns, poor contrast, no clear hierarchy. If you have ever rage-quit an app because you could not find the settings menu, you have experienced bad UI firsthand. The gap between those two outcomes is, in almost every case, a design decision someone made or skipped.&lt;/p&gt;




&lt;p&gt;The best way to understand UI design is to start noticing it. Every app you use made hundreds of visual decisions. Some of them were careful. Some were rushed. Once you can see the difference, the field starts making a lot more sense — and the work starts feeling worth doing.&lt;/p&gt;

&lt;p&gt;For a deeper look at how UI fits into the full design and development workflow, the &lt;a href="https://www.nngroup.com/articles/definition-user-interface-design/" rel="noopener noreferrer"&gt;Nielsen Norman Group&lt;/a&gt; has some of the most rigorous writing on the subject available online.&lt;/p&gt;

</description>
      <category>ui</category>
      <category>ux</category>
      <category>design</category>
      <category>webdev</category>
    </item>
    <item>
      <title>DevOps Pipeline: Stages, Tools, and CI/CD Explained</title>
      <dc:creator>ilyas Elaissi</dc:creator>
      <pubDate>Tue, 09 Jun 2026 19:15:51 +0000</pubDate>
      <link>https://dev.to/ilyas_elaissi/devops-pipeline-stages-tools-and-cicd-explained-3fpm</link>
      <guid>https://dev.to/ilyas_elaissi/devops-pipeline-stages-tools-and-cicd-explained-3fpm</guid>
      <description>&lt;p&gt;Most teams don't fail at writing code. They fail at getting it to production reliably, quickly, and without someone staying late to babysit a deployment script. A well-constructed DevOps pipeline is the answer to that specific problem — and once you've set one up properly, you'll wonder how you survived the manual version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The Core Idea Behind Automated Delivery&lt;/li&gt;
&lt;li&gt;What a DevOps Pipeline Actually Looks Like&lt;/li&gt;
&lt;li&gt;From Code to Production: The Real Stages&lt;/li&gt;
&lt;li&gt;Choosing the Right Tools for Each Stage&lt;/li&gt;
&lt;li&gt;How to Build a DevOps Pipeline Step by Step&lt;/li&gt;
&lt;li&gt;Azure Pipelines and YAML-Based Workflows&lt;/li&gt;
&lt;li&gt;Frequently Asked Questions&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Core Idea Behind Automated Delivery
&lt;/h2&gt;

&lt;p&gt;Before automation, the software delivery lifecycle looked like this: a developer finishes a feature, hands it off, a build engineer compiles it manually, someone spins up a test environment from a wiki page, a tester runs scripts by hand, and two weeks later — if nothing broke — the code might reach production. Every step was a human dependency. Every human dependency was a potential failure point.&lt;/p&gt;

&lt;p&gt;The whole point of continuous integration and delivery is to break that dependency chain. You commit code, the system takes over, and the pipeline carries that commit through build, test, and deployment automatically. The goal isn't speed for its own sake. It's repeatability. A process that runs identically every time is far less error-prone than one that depends on who's on shift.&lt;/p&gt;

&lt;p&gt;What drives the workflow is automation at each hand-off point. Instead of a build engineer, you have a build server. Instead of a manual test run, you have a test suite that fires on every commit. Instead of an ops engineer SSHing into a box, you have a deployment script triggered by a passing quality gate. The whole thing becomes a conveyor belt, not a relay race where someone might drop the baton.&lt;/p&gt;

&lt;p&gt;The architecture behind this isn't complicated to understand in principle. What makes it hard is the integration — wiring the right tools together at each stage, setting up the triggers correctly, and making sure failures surface fast enough that the developer who caused them hasn't already moved on to something else. That last part is what continuous feedback is really about: shrinking the time between a broken commit and the developer knowing about it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What a DevOps Pipeline Actually Looks Like
&lt;/h2&gt;

&lt;p&gt;A CI/CD pipeline diagram typically shows a linear flow: source code → build → test → deploy → monitor. That's accurate but slightly misleading because it implies each stage waits neatly for the previous one. In practice there's parallelism, branching, and rollback paths everywhere.&lt;/p&gt;

&lt;p&gt;Here's the conceptual shape of the thing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Developer pushes code to a version control system (Git, most likely).&lt;/li&gt;
&lt;li&gt;A commit triggers the CI server, which pulls the latest code from the repository and starts a build.&lt;/li&gt;
&lt;li&gt;The build artifact gets tested — unit tests first, then integration, then functional.&lt;/li&gt;
&lt;li&gt;If tests pass, the artifact moves through quality gates before being deployed to a staging or UAT environment.&lt;/li&gt;
&lt;li&gt;Further testing (security, performance, acceptance) runs against the deployed build.&lt;/li&gt;
&lt;li&gt;On a green result, the artifact is promoted to production.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Monitoring and feedback run the entire length of that chain. Every stage should be producing signals that flow back to the team. When something breaks at stage 3, the developer should know within minutes, not the next morning.&lt;/p&gt;

&lt;p&gt;The important thing about that linear diagram is what it's replaced: a collection of manual handoffs, each with its own tribal knowledge and its own failure modes. When you visualize the pipeline this way, it's easy to spot where a team is still doing things by hand. Those gaps are where you focus first.&lt;/p&gt;

&lt;p&gt;👉 Also read: &lt;a href="https://codetips.dev/ci-cd-vs-devops" rel="noopener noreferrer"&gt;CI/CD vs DevOps: What's the Difference and Which One Do You Need?&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  From Code to Production: The Real Stages
&lt;/h2&gt;

&lt;p&gt;The DevOps pipeline stages from code to production break down into roughly five areas, though different teams name them differently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Source and Integration
&lt;/h3&gt;

&lt;p&gt;Everything starts with version control. The developer commits to a branch in Git or a similar system. The version control repository is the single source of truth — no code exists outside it, and nothing gets built from a local machine. That rule alone eliminates whole categories of "works on my machine" problems.&lt;/p&gt;

&lt;p&gt;Continuous integration means every commit to the main branch (or a pull request targeting it) triggers an automated build. No exceptions. The moment you start allowing "quick fixes" to bypass the CI process, you've broken the contract the pipeline is built on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build
&lt;/h3&gt;

&lt;p&gt;The build stage compiles source code into a deployable artifact. For a Java project, Maven handles this well — it manages dependencies, compiles the code, and packages it into a JAR or WAR. For other stacks, equivalents exist (npm, Gradle, Cargo, and so on). The build should be hermetic: given the same inputs, it produces the same output every time. If your build behaves differently depending on what's cached on the build server, that's a problem to fix before anything else.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test
&lt;/h3&gt;

&lt;p&gt;This is where most pipelines either earn their keep or become a bottleneck. The test stage should run unit tests (JUnit is the standard for Java), then integration tests, then broader functional tests. Selenium handles browser-based functional testing well. Cucumber and similar tools cover behavior-driven scenarios that map to actual acceptance criteria.&lt;/p&gt;

&lt;p&gt;Quality gates are the checkpoints here. SonarQube, for example, lets you define rules — minimum code coverage, no critical security issues, no new bugs above a certain threshold — and block the pipeline if those rules aren't met. That's not bureaucracy; that's the pipeline doing its job.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy
&lt;/h3&gt;

&lt;p&gt;Deployment targets vary enormously: a VMware cluster, an AWS environment, a Rackspace setup, a Kubernetes cluster running containerized app deployments. The specific target matters less than whether the deployment is automated and idempotent. Running it twice should produce the same result as running it once.&lt;/p&gt;

&lt;p&gt;Configuration management tools like Puppet and Chef handle environment setup — installing packages, managing config files, ensuring the target environment matches expectations before the artifact lands on it. Infrastructure as code extends this further: your environment definition lives in version control just like your application code, and it's provisioned automatically. No more "we set that up six months ago and nobody remembers how."&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitor and Feedback
&lt;/h3&gt;

&lt;p&gt;Continuous operations means the pipeline doesn't stop at deployment. Monitoring tools watch the running application and feed alerts back into the same communication channels the team uses. If a deployment causes a spike in error rates, someone should know before a user reports it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Choosing the Right Tools for Each Stage
&lt;/h2&gt;

&lt;p&gt;There's no universal stack. The right tools depend on your language, your team size, your cloud provider, and what you're already running. That said, some tools show up in most conversations for good reasons.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CI servers:&lt;/strong&gt; Jenkins is the most widely deployed option and has a plugin ecosystem covering almost every tool you'd need to integrate. It's been around long enough that the documentation is comprehensive and most edge cases have a Stack Overflow answer. Bamboo is a reasonable alternative for teams already on the Atlassian stack. Travis CI works well for open-source projects hosted on GitHub. GitHub Actions has become the default for many teams starting fresh — it lives where your code lives, which removes one integration layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build tools:&lt;/strong&gt; Maven for Java projects is the standard. It handles dependency resolution and build lifecycle well enough that most teams don't need to think about it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code quality:&lt;/strong&gt; SonarQube for static analysis and quality gates. You can self-host it or use SonarCloud if you'd rather not manage the infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Testing:&lt;/strong&gt; JUnit for unit tests, Selenium for browser automation, Cucumber for BDD-style acceptance tests. Most CI servers have plugins for all three.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuration management:&lt;/strong&gt; Puppet and Chef both solve the same problem — consistent environment state across machines. The choice between them usually comes down to which one the ops team already knows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Containers and cloud:&lt;/strong&gt; Docker for containerization, Kubernetes for orchestration. AWS, GCP, and Azure are all viable deployment targets; your choice is mostly determined by where you're already spending money.&lt;/p&gt;

&lt;p&gt;👉 Also read: &lt;a href="https://codetips.dev/ci-cd-tools-for-devops" rel="noopener noreferrer"&gt;The Best CI/CD Tools for DevOps Teams in 2024&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a more comprehensive breakdown of pipeline tooling options, the &lt;a href="https://landscape.cncf.io/" rel="noopener noreferrer"&gt;CNCF Cloud Native Landscape&lt;/a&gt; is a useful (if overwhelming) reference for the ecosystem.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Build a DevOps Pipeline Step by Step
&lt;/h2&gt;

&lt;p&gt;Building a DevOps pipeline from scratch sounds intimidating. It's not, if you approach it incrementally rather than trying to automate everything at once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start with source control.&lt;/strong&gt; If your team doesn't have a disciplined branching strategy and a rule that all code goes through the repository before it goes anywhere else, fix that first. Nothing else works without it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add a CI server.&lt;/strong&gt; Get Jenkins or GitHub Actions running and configure it to trigger on every push to your main branch. At this point it doesn't need to do anything complex — just a successful trigger proves the integration works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automate the build.&lt;/strong&gt; Wire your build tool (Maven, npm, whatever applies) into the CI server. Every triggered pipeline run should produce a build artifact. Failures should notify the committer immediately, not accumulate in an ignored email folder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add tests.&lt;/strong&gt; Start with unit tests because they're fast and the feedback loop is tight. A JUnit suite that runs in 90 seconds is immediately useful. Integration and functional tests come later — they're slower, and running them on every commit becomes impractical beyond a certain scale without parallelization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add quality gates.&lt;/strong&gt; Connect SonarQube or a similar tool and define the rules your team agrees on. Start conservative — it's easier to loosen rules later than to add them after people have gotten used to ignoring them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automate deployment to a non-production environment.&lt;/strong&gt; Get a staging environment that's provisioned automatically (using Puppet, Chef, or infrastructure-as-code tooling like Terraform) and deploy every passing build there. Once this works reliably, add the acceptance and security testing that runs against it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automate production deployment.&lt;/strong&gt; This is the last step, and it's the one most teams are nervous about. The nervousness usually signals that the earlier stages aren't trusted yet. If your test coverage is solid and your staging environment reliably catches issues, automated production deployment is just the logical extension of everything you've already built.&lt;/p&gt;

&lt;p&gt;The key word across all of this is incremental. A team that goes from zero to full pipeline automation in one sprint usually ends up with something fragile. A team that adds one stage per iteration ends up with something they actually understand and trust.&lt;/p&gt;




&lt;h2&gt;
  
  
  Azure Pipelines and YAML-Based Workflows
&lt;/h2&gt;

&lt;p&gt;The Azure CI/CD pipeline offering from Microsoft is worth understanding separately because it makes some choices that differ from Jenkins-style setups. In Azure Pipelines, your pipeline definition lives in a YAML file committed to your repository alongside your code. That's a meaningful shift from clicking through a UI to configure jobs.&lt;/p&gt;

&lt;p&gt;A minimal Azure Pipeline YAML file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;trigger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;vmImage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ubuntu-latest'&lt;/span&gt;

&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Maven@3&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mavenPomFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pom.xml'&lt;/span&gt;
      &lt;span class="na"&gt;goals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;clean&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;package'&lt;/span&gt;
      &lt;span class="na"&gt;publishJUnitResults&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;testResultsFiles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/TEST-*.xml'&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SonarQubePrepare@5&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;SonarQube&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SonarQubeConnection'&lt;/span&gt;
      &lt;span class="na"&gt;scannerMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Auto'&lt;/span&gt;
      &lt;span class="na"&gt;projectKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my-project'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;trigger&lt;/code&gt; block defines which branches kick off the pipeline. &lt;code&gt;pool&lt;/code&gt; sets the agent environment. Each step maps to a task — either a built-in task like &lt;code&gt;Maven@3&lt;/code&gt; or a custom script. Agents can be Microsoft-hosted (no infrastructure to manage) or self-hosted (you control the machine).&lt;/p&gt;

&lt;p&gt;The YAML-as-code approach means your pipeline definition goes through the same review process as your application code. You can see the history of pipeline changes, revert a bad pipeline update, and branch your pipeline definition alongside feature branches. For teams already comfortable with Git workflows, this model fits naturally.&lt;/p&gt;

&lt;p&gt;Azure Pipelines also integrates with GitHub repositories natively, so if you're not in the Azure DevOps ecosystem but want to use it for pipelines, you can connect your GitHub repo without migrating anything.&lt;/p&gt;

&lt;p&gt;The trade-off is that Azure Pipeline YAML has its own syntax quirks and a learning curve that's steeper than GitHub Actions for developers who already live in the GitHub UI. For a devops pipeline tutorial for beginners, GitHub Actions is probably the gentler on-ramp; Azure Pipelines is the better choice when you need deeper integration with Azure services or need enterprise-grade access controls.&lt;/p&gt;




&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is a DevOps pipeline and how does it work?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A DevOps pipeline is the automated chain of tools and processes that carries code from a developer's commit through build, test, and deployment stages until it reaches production. It works by using triggers: a push to a repository starts the build server, the build server runs tests, and passing tests trigger deployment. Each stage produces feedback that flows back to the team. The key word is "automated" — a pipeline that requires a human to kick off each stage is really just a checklist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which CI server should a beginner start with?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Honestly, GitHub Actions if your code is on GitHub. It's the lowest-friction starting point — you create a &lt;code&gt;.github/workflows&lt;/code&gt; directory, add a YAML file, and you have a working pipeline within an hour. Jenkins gives you more control and more plugin options, but setting it up from scratch takes longer and involves more infrastructure decisions upfront. Start with what gets you running fastest; you can always migrate later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need infrastructure as code to have a working pipeline?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No, but you'll probably want it within six months of going without it. The moment you have to recreate a staging environment from memory, you'll wish it was in version control. Tools like Terraform work well alongside configuration management tools like Puppet and Chef. You don't have to implement everything at once — automate the environment setup for your staging environment first, then work backward or forward from there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the difference between continuous delivery and continuous deployment?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Continuous delivery means every passing build is &lt;em&gt;ready&lt;/em&gt; to go to production and can be deployed with one click or one command. Continuous deployment goes one step further: passing builds are deployed to production automatically, without any human approval step. Most regulated industries (finance, healthcare) stop at continuous delivery because they need a manual sign-off gate before production. Most software-as-a-service teams aim for continuous deployment because it keeps release batches small and rollbacks trivial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How long does building a pipeline from scratch actually take?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A basic CI pipeline that builds and runs unit tests on every commit can be functional in a day for a small project. A full pipeline with automated deployment, quality gates, security scanning, and environment automation takes weeks — but you don't build it all at once. Most teams get roughly 80% of the value from the first few stages (automated build and unit tests). The rest is worth building, but the returns diminish as you go further.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>ai</category>
      <category>security</category>
    </item>
    <item>
      <title>JavaScript Objects &amp; Methods Explained with Examples</title>
      <dc:creator>ilyas Elaissi</dc:creator>
      <pubDate>Fri, 05 Jun 2026 21:29:12 +0000</pubDate>
      <link>https://dev.to/ilyas_elaissi/javascript-objects-methods-explained-with-examples-56cg</link>
      <guid>https://dev.to/ilyas_elaissi/javascript-objects-methods-explained-with-examples-56cg</guid>
      <description>&lt;p&gt;An object in JavaScript is just a bag of named values. That is the entire idea. Once that clicks, JavaScript Objects &amp;amp; Methods stop feeling like a topic and start feeling like the default way you model anything real, whether that's a coffee machine, a user session, or a delivery van with a driver inside it.&lt;/p&gt;

&lt;p&gt;I'll walk through the whole thing using a car. Same example I learned on, slightly different names so we are not all writing the same Ford Model T for the hundredth time. By the end you'll know how to spin up objects, attach behavior to them, point one object at another, and use the built-ins like &lt;code&gt;Object.keys()&lt;/code&gt; and &lt;code&gt;Object.entries()&lt;/code&gt; without looking them up every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  A car is a good first object
&lt;/h2&gt;

&lt;p&gt;Here is a car as a plain object literal. This is the object initializer syntax, the curly-brace form most people reach for first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;car&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tesla&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Model 3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1611&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;isElectric&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each line inside the braces is a property paired with a value. &lt;code&gt;brand&lt;/code&gt; is a key, &lt;code&gt;"Tesla"&lt;/code&gt; is its value, and that pairing is the whole contract: a name on the left, anything you want on the right. Numbers, strings, booleans, arrays, other objects, functions. JavaScript does not care.&lt;/p&gt;

&lt;p&gt;Every car you ever build will share the same set of property names. The values change. That's the difference between a class of thing and an instance of it. You and I are both "person" shaped. Our values for &lt;code&gt;firstName&lt;/code&gt; differ.&lt;/p&gt;

&lt;p&gt;This is what people mean by an object-based paradigm in JavaScript. You describe the world as a bunch of named bundles, and you give those bundles behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading and writing properties
&lt;/h2&gt;

&lt;p&gt;Two ways to read a property. The first is dot notation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// "Model 3"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second is bracket notation, where the property name is a string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;model&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// "Model 3"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The practical difference between dot notation and bracket notation in JavaScript comes down to two cases. Use brackets when the key has spaces or special characters (&lt;code&gt;car["max speed"]&lt;/code&gt;), or when the key is in a variable you do not know until runtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;weight&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// 1611&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Outside those cases, dot access is shorter and easier to read. I use brackets maybe one time in twenty.&lt;/p&gt;

&lt;p&gt;Writing is the same shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;midnight blue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;year&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2023&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the key did not exist before, JavaScript creates it. No declaration, no schema, no ceremony. This is freeing and occasionally terrible, which we'll come back to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Giving the car something to do
&lt;/h2&gt;

&lt;p&gt;A property whose value is a function is a method. That is the whole distinction. A function sitting on the wall is a function; a function sitting on an object is a method.&lt;/p&gt;

&lt;p&gt;Here is how to add methods to a JavaScript object directly on the literal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;car&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tesla&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Model 3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;isDriving&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;drive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isDriving&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is driving`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isDriving&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; stopped`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drive&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Tesla Model 3 is driving&lt;/span&gt;
&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// Tesla Model 3 stopped&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;drive()&lt;/code&gt; and &lt;code&gt;stop()&lt;/code&gt; shorthand is just a cleaner way to write &lt;code&gt;drive: function() {...}&lt;/code&gt;. They behave identically.&lt;/p&gt;

&lt;p&gt;Now the part that trips up almost everyone the first time.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does the &lt;code&gt;this&lt;/code&gt; keyword actually work?
&lt;/h2&gt;

&lt;p&gt;Inside &lt;code&gt;drive&lt;/code&gt;, &lt;code&gt;this&lt;/code&gt; refers to whatever object the method was called on. &lt;code&gt;car.drive()&lt;/code&gt; means "run &lt;code&gt;drive&lt;/code&gt;, and while you do, &lt;code&gt;this&lt;/code&gt; is &lt;code&gt;car&lt;/code&gt;." So &lt;code&gt;this.isDriving = true&lt;/code&gt; is the same as &lt;code&gt;car.isDriving = true&lt;/code&gt; in that moment.&lt;/p&gt;

&lt;p&gt;If you ripped the method out and called it standalone, &lt;code&gt;this&lt;/code&gt; would no longer point at &lt;code&gt;car&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;looseDrive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;drive&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;looseDrive&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// this is undefined (in strict mode) or the global object&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the most common gotcha in the language. The &lt;code&gt;this&lt;/code&gt; keyword in JavaScript is not bound to where the function was defined. It is bound to how the function was called. Method call uses dot, &lt;code&gt;this&lt;/code&gt; is the thing on the left of the dot. That rule will get you through 90% of cases.&lt;/p&gt;

&lt;p&gt;Arrow functions are the exception worth knowing. An arrow function does not get its own &lt;code&gt;this&lt;/code&gt;; it inherits from the surrounding scope. So this breaks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;car&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tesla&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;drive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// undefined&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use regular function syntax for methods. Use arrows for callbacks inside methods. That is the safe default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building many cars with a constructor function
&lt;/h2&gt;

&lt;p&gt;The literal approach is fine for one-off objects. The moment you want fifty cars, copy-pasting becomes painful. This is where the constructor function pattern earns its keep.&lt;/p&gt;

&lt;p&gt;A constructor is a normal function you call with &lt;code&gt;new&lt;/code&gt;. Convention: capitalize the first letter so you can spot it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isElectric&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;brand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;weight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isElectric&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isElectric&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isDriving&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;drive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isDriving&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is driving`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isDriving&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; stopped`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;car1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tesla&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Model 3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1611&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;car2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Toyota&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Corolla&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1340&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;car1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drive&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Tesla Model 3 is driving&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isElectric&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;new&lt;/code&gt; keyword does four things, and it is worth knowing all of them. It creates an empty object, sets &lt;code&gt;this&lt;/code&gt; inside the function to that new object, runs the function body so your assignments populate it, and finally returns the object. Skip &lt;code&gt;new&lt;/code&gt; and &lt;code&gt;this&lt;/code&gt; will be wrong, the function will return &lt;code&gt;undefined&lt;/code&gt;, and your variable will be empty. I have lost an hour to that mistake more than once.&lt;/p&gt;

&lt;p&gt;That is the JavaScript object constructor function explained in one paragraph. Modern code often uses &lt;code&gt;class&lt;/code&gt; syntax instead, but &lt;code&gt;class&lt;/code&gt; is mostly cosmetic sugar over this exact pattern. If you understand constructors, classes are a five-minute upgrade later.&lt;/p&gt;

&lt;p&gt;One small efficiency note. Defining &lt;code&gt;drive&lt;/code&gt; and &lt;code&gt;stop&lt;/code&gt; inside the constructor means every car gets its own copy of those functions. For two cars, who cares. For ten thousand, you would want the methods on a shared prototype using &lt;code&gt;Car.prototype.drive = function() {...}&lt;/code&gt;. That is object inheritance through the prototype chain, and it's where the real difference between static vs instance methods shows up in practice. Worth reading about on the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_objects" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt; once you are comfortable with the basics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting an object inside an object
&lt;/h2&gt;

&lt;p&gt;Real things are not flat. A car has a driver. The driver has a name. The name has a first and last part. You model this by storing objects as values of other objects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;brand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; entered the car`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;car1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tesla&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Model 3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;person1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bob&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Miller&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;car1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;person1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Bob Miller entered the car&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Bob&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reaching into nested objects in JavaScript is just dot-chaining: &lt;code&gt;car1.driver.firstName&lt;/code&gt;. The catch is that if any link in the chain is &lt;code&gt;undefined&lt;/code&gt; or &lt;code&gt;null&lt;/code&gt;, you get a runtime error. The optional chaining operator handles that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Bob&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// undefined, no crash&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;?.&lt;/code&gt; has saved me a depressing number of null-check &lt;code&gt;if&lt;/code&gt; statements. Use it freely when you are not sure a property exists.&lt;/p&gt;

&lt;p&gt;If you want to know how to access nested objects in JavaScript when the keys are dynamic, fall back to bracket notation at each level: &lt;code&gt;car1["driver"]["firstName"]&lt;/code&gt;. It is the same idea, just spelled out.&lt;/p&gt;

&lt;p&gt;👉 Also read: &lt;a href="https://codetips.dev/how-to-detect-word-wrap-in-textarea-javascript" rel="noopener noreferrer"&gt;How to detect word wrap in a textarea with JavaScript&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The built-in Object methods you will actually use
&lt;/h2&gt;

&lt;p&gt;The global &lt;code&gt;Object&lt;/code&gt; has a bunch of static helpers. These are the ones I reach for weekly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;Object.keys(obj)&lt;/code&gt;&lt;/strong&gt; returns an array of the property names. &lt;strong&gt;&lt;code&gt;Object.values(obj)&lt;/code&gt;&lt;/strong&gt; returns an array of the values. &lt;strong&gt;&lt;code&gt;Object.entries(obj)&lt;/code&gt;&lt;/strong&gt; returns an array of &lt;code&gt;[key, value]&lt;/code&gt; pairs. Once you know how to use &lt;code&gt;Object.keys&lt;/code&gt; and &lt;code&gt;Object.values&lt;/code&gt; in JavaScript, iterating an object stops being a chore:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;car&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tesla&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Model 3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1611&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// ["brand", "model", "weight"]&lt;/span&gt;
&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ["Tesla", "Model 3", 1611]&lt;/span&gt;
&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// [["brand", "Tesla"], ["model", "Model 3"], ["weight", 1611]]&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;Object.assign(target, source)&lt;/code&gt;&lt;/strong&gt; copies properties from source to target. Mostly replaced by the spread operator merging trick below, but still useful when you want to mutate an existing object on purpose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;Object.freeze(obj)&lt;/code&gt;&lt;/strong&gt; locks an object so its properties cannot be changed, added, or removed. Handy for constants you actually want to be constant. Note it is shallow: nested objects can still be mutated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;Object.create(proto)&lt;/code&gt;&lt;/strong&gt; builds a new object with a specific prototype. This is also how you make null-prototype objects with &lt;code&gt;Object.create(null)&lt;/code&gt;, which are useful as pure dictionaries because they do not inherit &lt;code&gt;toString&lt;/code&gt;, &lt;code&gt;hasOwnProperty&lt;/code&gt;, and friends. If you have ever had a user submit &lt;code&gt;__proto__&lt;/code&gt; as a form key, you understand why this matters.&lt;/p&gt;

&lt;p&gt;The spread operator is the everyday workhorse for combining objects without mutating them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tesla&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Model 3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;upgraded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2023&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;// { brand: "Tesla", model: "Model 3", color: "red", year: 2023 }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Later keys overwrite earlier ones, which is the whole pattern behind config defaults and React state updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getters, setters, and computed properties
&lt;/h2&gt;

&lt;p&gt;Sometimes a property should look static from the outside but actually run code when you read or write it. That is what getters and setters are for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;car&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tesla&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Model 3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// "Tesla Model 3"&lt;/span&gt;
&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fullName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Toyota Corolla&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// "Toyota"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You access &lt;code&gt;car.fullName&lt;/code&gt; like a property, no parentheses, but a function runs underneath. I use this sparingly because it hides behavior behind what looks like a field, and that surprise is worth the cost only when the abstraction is genuinely cleaner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where objects get weird
&lt;/h2&gt;

&lt;p&gt;A few things will bite you. Worth knowing up front.&lt;/p&gt;

&lt;p&gt;Comparing two objects with &lt;code&gt;===&lt;/code&gt; checks reference identity, not contents. &lt;code&gt;{a: 1} === {a: 1}&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;. Even when every key and value matches, they are different objects in memory. To compare contents you either write your own check or use a library.&lt;/p&gt;

&lt;p&gt;Object coercion happens when JavaScript needs a primitive from an object, like in &lt;code&gt;alert(obj)&lt;/code&gt; or &lt;code&gt;"" + obj&lt;/code&gt;. By default you get &lt;code&gt;"[object Object]"&lt;/code&gt;, which is the kind of bug that ships to production. Define a &lt;code&gt;toString()&lt;/code&gt; method if you care.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;typeof&lt;/code&gt; lies about objects. &lt;code&gt;typeof null&lt;/code&gt; is &lt;code&gt;"object"&lt;/code&gt;. Arrays are objects too: &lt;code&gt;typeof []&lt;/code&gt; is &lt;code&gt;"object"&lt;/code&gt;. Use &lt;code&gt;Array.isArray()&lt;/code&gt; for arrays and an explicit &lt;code&gt;=== null&lt;/code&gt; check for null.&lt;/p&gt;

&lt;p&gt;Mutating an object passed into a function changes it for the caller. Objects are passed by reference. If you want a safe copy, &lt;code&gt;{ ...original }&lt;/code&gt; gets you a shallow clone. For deep structures, &lt;code&gt;structuredClone(obj)&lt;/code&gt; is now built into modern browsers and Node, and it actually works on nested data, dates, and Maps.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small beginner guide to JavaScript objects and methods, condensed
&lt;/h2&gt;

&lt;p&gt;If you want the minimum viable mental model, it is this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An object is named values inside braces.&lt;/li&gt;
&lt;li&gt;A method is a function that lives on an object.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;this&lt;/code&gt; inside a method is the object you called the method on.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;new ConstructorFn(...)&lt;/code&gt; builds a fresh object using a function as a template.&lt;/li&gt;
&lt;li&gt;Nested objects are just values that happen to be objects. Walk them with dots or &lt;code&gt;?.&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Object.keys()&lt;/code&gt;, &lt;code&gt;Object.values()&lt;/code&gt;, and &lt;code&gt;Object.entries()&lt;/code&gt; turn objects into arrays you can loop over.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sites like W3Schools and GeeksforGeeks have decent quick-reference tables if you want a second angle on the syntax. For deeper specifics, MDN is the source I trust.&lt;/p&gt;

&lt;p&gt;The last thing I'd push you toward is just building something with this. Model a playlist, a shopping cart, a chess board, anything where there are clear nouns and verbs. Modeling real-world things as objects is where the abstractions start to feel obvious instead of academic. The first time you reach for an object without thinking about it, you are done learning the basics.&lt;/p&gt;

&lt;p&gt;Then go break a few. Pass methods around without their objects. Watch &lt;code&gt;this&lt;/code&gt; disappear. Try to mutate a frozen object. The error messages teach faster than any tutorial, including this one.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Best AI for Code: Top 4 Tools Tested and Ranked</title>
      <dc:creator>ilyas Elaissi</dc:creator>
      <pubDate>Thu, 28 May 2026 23:09:47 +0000</pubDate>
      <link>https://dev.to/ilyas_elaissi/best-ai-for-code-top-4-tools-tested-and-ranked-3cha</link>
      <guid>https://dev.to/ilyas_elaissi/best-ai-for-code-top-4-tools-tested-and-ranked-3cha</guid>
      <description>&lt;p&gt;Most AI coding comparisons test "Hello World" apps and call it a day. I ran every major tool through the same three-stage gauntlet: a simple build, a complex full-stack application, and multiple rounds of revisions. The best AI for code should hold up under all three. Most do not.&lt;/p&gt;

&lt;p&gt;Here is what I found, scored using a 100-point rubric across four equal categories: interface and experience, AI agent effectiveness, deployment, and pricing. No favorites going in. The scores reflect what actually happened on screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;How the Testing Actually Worked&lt;/li&gt;
&lt;li&gt;Cursor: The Developer's Workhorse&lt;/li&gt;
&lt;li&gt;Windsurf: Clean UI, Shaky Under Pressure&lt;/li&gt;
&lt;li&gt;GitHub Copilot: The Best AI for Code Inside Your Existing IDE&lt;/li&gt;
&lt;li&gt;Base44: Where No-Code Actually Becomes a Full Product&lt;/li&gt;
&lt;li&gt;How These Tools Stack Up (and Who Should Use What)&lt;/li&gt;
&lt;li&gt;Frequently Asked Questions&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How the Testing Actually Worked
&lt;/h2&gt;

&lt;p&gt;Every platform went through the same four-category report card, 25 points each, 100 total.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Category 1: UX and Interface (25 points).&lt;/strong&gt; First impressions matter, but so does how the environment holds up after 20 minutes of real use. Does it create friction before you even start building?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Category 2: AI Agent Effectiveness (25 points).&lt;/strong&gt; This is the most important category. Three structured prompt stages: a simple app, a complex Reddit-style MVP with authentication, offline mode, and threading, then layered revisions (light/dark toggle, chatbot integration, full redesign while preserving functionality). Real-world testing methodology means pushing until things break, not stopping at the first green checkmark.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Category 3: Code Export and Deployment (25 points).&lt;/strong&gt; Generation means nothing if shipping requires three hours of manual setup. One-click deployment, native hosting, and code export options all count here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Category 4: Pricing and Limitations (25 points).&lt;/strong&gt; Compared against the actual cost of hiring a developer ($50K to $150K per year) or an agency ($10K to $100K per project). Value has to be real, not just "cheaper than a senior engineer."&lt;/p&gt;

&lt;p&gt;The testing used the exact same prompts in the exact same order for every platform. No advantages, no shortcuts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cursor: The Developer's Workhorse
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Final score: 68/100&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cursor is built on top of VS Code, and you can feel it immediately. The file explorer sits on the left, the editor in the center, the AI chat panel on the right. For anyone who already lives inside a traditional IDE, there is almost no learning curve. The layout is logical and the tooling is familiar.&lt;/p&gt;

&lt;p&gt;For someone without a development background, it is another story. The interface is dense. Navigating it without prior context takes real time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interface score: 19/25&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The prompt tests exposed a consistent weakness: accuracy under layered instructions. The simple app produced something functional but primitive, closer to a file directory than a finished product. The complex build was missing offline functionality that was explicitly requested. That is a direct failure to follow the prompt, not a minor visual issue.&lt;/p&gt;

&lt;p&gt;The light/dark mode toggle worked partially but left several UI sections stuck in dark mode regardless of which state was active. The chatbot integration was actually clean. The full redesign request broke the layout entirely.&lt;/p&gt;

&lt;p&gt;Cursor shows genuine capability, especially when prompts are surgical. But prompt building efficiency drops noticeably when revisions stack on top of each other. Layered revision handling is where it falls apart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI agent score: 13/25&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Deployment in Cursor is manual. There is no native one-click publishing. Connecting to Netlify requires installing the extension yourself and configuring it. For experienced developers, that is fine. For anyone trying to move from build to production without a devops background, it adds real friction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deployment score: 17/25&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cursor's Pro plan runs $16 to $20 per month. Pro Plus is $60. Ultra is $200. In June 2025, Cursor switched to a credit-based billing model, which cut the effective request count on the $20 plan from roughly 500 down to about 225. That makes usage harder to predict, especially for heavier workflows. Credit-based billing sounds flexible until you hit the ceiling mid-project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing score: 19/25&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cursor is dramatically cheaper than any human developer and it can accelerate development by 30 to 40% for people who already know how to code. But it enhances developers rather than replacing the need for one. If you are not already technical, Cursor is not the tool that changes that.&lt;/p&gt;




&lt;h2&gt;
  
  
  Windsurf: Clean UI, Shaky Under Pressure
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Final score: 73/100&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Windsurf also runs on VS Code. The layout is nearly identical to Cursor: file explorer left, editor center, AI agent (called Cascade) on the right. It is clean and well-organized, and the AI integration sits naturally inside the workflow. Like Cursor, it reads as dense to non-technical users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interface score: 19/25&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The simple build came together in about three minutes and looked professional at first glance. Looking closer, it was a display-layer application rather than a real tracking tool. No actual logging or bug-tracking system underneath. Minor background rendering issues too.&lt;/p&gt;

&lt;p&gt;The complex Reddit-style build was a genuine bright spot. Windsurf included offline preview functionality for both posting and viewing posts, which was explicitly required. That is a meaningful win. It did lack placeholder data and deeper threading structures that would make the app feel real rather than scaffolded.&lt;/p&gt;

&lt;p&gt;The light/dark toggle was clean. The chatbot integration worked without breaking anything. Then came the full redesign request, and Windsurf stumbled the same way Cursor did. Parts of the site broke. The layout became unstable. You could reprompt it to fix things, but needing a correction run after a major revision is a stability problem worth flagging. It signals the AI does not hold the full application context when the scope of a change gets large enough.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI agent score: 15/25&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Windsurf's deployment story is meaningfully better than Cursor's. It includes native Netlify support built directly into the interface. No extensions to install, no manual configuration. That removes several steps that would otherwise gate non-technical users from going live.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deployment score: 20/25&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The free plan offers 25 credits, which burn out in roughly three days of normal coding usage. The Pro plan is $15 per month ($180 per year) with 500 credits. Still far cheaper than any agency or full-time developer, but the credit ceiling creates the same unpredictability as Cursor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing score: 19/25&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Windsurf is a stronger all-around package than Cursor, particularly for users who want smoother deployment. But it still assumes you have technical oversight somewhere in the loop.&lt;/p&gt;




&lt;h2&gt;
  
  
  GitHub Copilot: The Best AI for Code Inside Your Existing IDE
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Final score: 81/100&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GitHub Copilot does not try to replace your IDE. It extends it. You install it as a plugin into whatever editor you already use, and it operates inside that environment. The UI stays whatever you are used to. There is no new interface to learn, no new file structure to navigate.&lt;/p&gt;

&lt;p&gt;That design choice is actually the clearest strength here. For developers, the onboarding friction is close to zero.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interface score: 21/25&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The simple build took about four minutes and produced something noticeably more polished than what Cursor or Windsurf generated at the same stage. Clean layout, production-ready output as a starting point, even without a true bug-tracking system underneath.&lt;/p&gt;

&lt;p&gt;The complex build completed in roughly seven minutes. Offline access was included without prompting. The initial version did not support posting, but adding it via a follow-up prompt worked cleanly. The light/dark toggle integrated across the entire site with no broken sections. The chatbot landed correctly on the first try. The full redesign request kept all features intact, with visual changes that were subtle but stable.&lt;/p&gt;

&lt;p&gt;Across all three stages, Copilot showed the best consistency of the IDE-based tools tested. The term "sonnet AI coding" gets thrown around a lot in discussions about model quality, and Copilot's Claude Sonnet-backed completions genuinely show in the output stability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI agent score: 23/25&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Copilot is an extension, not a deployment platform. Shipping still depends on your host IDE's tools and extensions. It can guide deployment to Netlify or similar platforms, but nothing is integrated natively. You need the external tooling already configured.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deployment score: 18/25&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Copilot Pro is $10 per month ($120 per year). Pro Plus is $39 per month. Business is $19 per user per month. Enterprise is $39 per user per month. For teams, note that GitHub repository hosting fees are separate. A 50-developer team combining GitHub and Copilot could run around $3,000 per month.&lt;/p&gt;

&lt;p&gt;Studies put productivity gains at roughly a 10.6% increase in pull requests per developer. At a $75 per hour developer rate, Copilot pays for itself if it saves a few minutes per day. That math works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing score: 19/25&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Copilot is the strongest option in this comparison for professional developers who want to move faster inside their existing setup. If you already know what you are doing, it is the most reliable accelerant in the group.&lt;/p&gt;

&lt;p&gt;👉 Also read: &lt;a href="https://codetips.dev/should-you-learn-to-code-with-ai" rel="noopener noreferrer"&gt;Should You Learn to Code With AI or Just Use the AI?&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Base44: Where No-Code Actually Becomes a Full Product
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Final score: 92/100&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Base44 is not an IDE. There is no installation, no extension configuration, no environment setup. You open the browser, describe what you want to build, and the platform handles the structure, logic, and implementation. A live preview updates in real time on the right. The AI chat manages instructions and revisions on the left. That is the entire interface.&lt;/p&gt;

&lt;p&gt;For anyone who has spent time with vibe coding workflow tools, this is what the category is actually supposed to feel like.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interface score: 24/25&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The simple build took about two minutes. Rather than generating a file-directory layout the way every other tool did, Base44 built an actual functional upload mechanism where users could take photos or upload images to identify insects. Mobile-optimized, clean, and usable from the first render. Not a placeholder. A product.&lt;/p&gt;

&lt;p&gt;The complex Reddit-style build included native authentication with login and signup pages, a working database for posts, offline preview mode, realistic placeholder data, and proper threading structures. Authentication handling and database integration were automatic. No manual wiring required, no configuration steps in between.&lt;/p&gt;

&lt;p&gt;The light/dark toggle worked perfectly, both themes rendered cleanly, no broken components. The chatbot integration required no external API keys or additional setup. The full redesign executed without breaking a single feature.&lt;/p&gt;

&lt;p&gt;That last point is worth pausing on. Every other platform in this test struggled or failed at the full redesign request. Base44 handled it cleanly. That is not a minor difference in output quality. It reflects how the platform holds application context across layered instructions.&lt;/p&gt;

&lt;p&gt;For users asking which AI coding tool is best for non developers, Base44 is the clearest answer. Full-stack generation, authentication, database, and deployment are all handled without requiring the user to understand any of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI agent score: 25/25&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Base44 supports native web deployment directly from within the platform. Authentication, database setup, login and signup pages are all generated automatically. No external hosting services to configure. It also supports direct publishing to iOS and Android, so mobile app deployment happens from the same interface where you built the app. Prototype to production without switching platforms or rebuilding anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deployment score: 25/25&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Base44 ranges from $192 to $1,920 per year ($16 to $160 per month billed annually). The Builder plan at $480 per year ($40 per month) is where most users land. It includes unlimited apps, custom domains, GitHub integration, and flat pricing without hidden infrastructure fees or unpredictable token usage.&lt;/p&gt;

&lt;p&gt;Functional apps realistically ship in 10 to 15 minutes. Production-ready applications take two to four hours. Traditional development cycles for comparable scope take weeks.&lt;/p&gt;

&lt;p&gt;The higher tiers are a real consideration for teams with advanced usage needs, which is why it does not score a perfect 25 here. But the all-in value at the Builder tier is hard to argue with.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing score: 18/25&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;👉 Also read: &lt;a href="https://codetips.dev/ai-productivity-myth" rel="noopener noreferrer"&gt;The AI Productivity Myth: What These Tools Actually Do to Your Output&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How These Tools Stack Up (and Who Should Use What)
&lt;/h2&gt;

&lt;p&gt;Here is the final AI coding ranking across all four tools tested:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fewoxszfadxriqt6z0330.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fewoxszfadxriqt6z0330.png" alt="comparison table" width="800" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A few things worth noting beyond the numbers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cursor and Windsurf&lt;/strong&gt; are genuinely useful if you already have a development background. They accelerate work. They do not replace the need for someone who understands architecture and deployment. If you are a developer who wants an AI-assisted workflow inside a familiar IDE environment, Cursor's VS Code integration and Windsurf's native Netlify deployment each have specific merits. A full Cursor AI vs GitHub Copilot vs Windsurf comparison for experienced developers would be a closer race than these scores suggest, because the deployment gap matters less when you already know how to configure hosting yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Copilot&lt;/strong&gt; is the best of the IDE-integrated tools. The accuracy under layered revisions separates it from Cursor and Windsurf in meaningful ways. For anyone asking about the best free AI coding assistant for beginners, it is worth noting that Copilot has a free tier (limited, but real), and the learning curve is minimal if you already use VS Code or JetBrains. It is also worth mentioning that Google Gemini Code Assist, Codeium, and Claude AI all operate in a similar category and are worth evaluating if you have specific language or IDE preferences. For Python work specifically, the best free AI for Python coding question often lands on Copilot or Codeium, depending on which IDE you live in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Base44&lt;/strong&gt; wins on the use case that the other tools cannot fully serve: someone who has an app idea and zero coding background. The fact that it handles full-stack generation, authentication, database setup, and multi-platform deployment without any configuration is not a marketing claim. It was demonstrated in the same tests that broke the other platforms. For anyone asking about the best AI coding tool for building full-stack apps without writing code, this is where the answer currently lives.&lt;/p&gt;

&lt;p&gt;For the best AI for code generation and debugging in a professional development context, Copilot is still the strongest choice. For everything else, Base44 closes the gap between idea and shipped product faster than anything else tested here.&lt;/p&gt;

&lt;p&gt;One honest caveat: Base44's higher pricing tiers are a real barrier for some users. If you need the best AI for coding free with no credit card, tools like Codeium and Replit both offer free tiers worth considering. Bolt.new and Lovable.dev are also worth a look for no-code app building, though they did not match Base44's consistency on the complex revision tests. And if you are looking for the best AI code generator free no limits, you will hit caps on every serious platform at some point. Free plans work for exploration, not for shipping.&lt;/p&gt;

&lt;p&gt;The tools that build fast but break on the hard prompt are not the best ai for code generation at scale. That distinction showed up clearly in testing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is the best AI for coding 2026 if I am a complete beginner?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Base44 is the clearest answer for non-technical users who want to ship something real. It handles authentication, databases, and deployment automatically. GitHub Copilot is the better pick if you are a beginner who already knows some code and wants to get faster. Copilot's IDE integration means you are still learning while getting AI assistance, which is actually a better learning setup than having the AI do everything invisibly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is there a good free AI code generator with no major limits?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Honestly, no. Every platform in this comparison caps free usage in some way. Windsurf's 25 free credits burn in about three days of normal use. Codeium and Replit have free tiers that go further, but both hit walls on complex projects. The best AI code generator free options work for prototyping and learning. For anything you want to actually ship, a paid plan is realistically required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does Claude AI for coding compare to these tools?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Claude AI (Anthropic's model) is not a standalone coding environment, but it powers the underlying intelligence in several of these platforms. The output quality when using Claude Sonnet specifically shows up in Copilot's revision stability. If you want Claude AI for coding directly, Cursor supports Claude models natively, and you can use the API directly in any environment. But you are adding your own tooling around it rather than getting a packaged workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which tool handles gemini AI coding well?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Google Gemini Code Assist is a separate product aimed at enterprise teams and integrates into JetBrains and VS Code. It was not included in this specific comparison, but it belongs in any serious AI coding ranking for teams already inside Google Cloud. For individual developers, Copilot currently has more mature IDE integration and a stronger track record on complex tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is Base44 actually worth the price compared to the free tools?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For someone building a real product without a development team, yes. The comparison is not Base44 versus a free AI tool. It is Base44 versus hiring a developer or an agency. At $480 per year, you are getting authentication handling, database setup, web deployment, and iOS/Android publishing included. An agency charges that for a few hours of work. The free tools are fine for experiments. Base44 is for shipping.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>news</category>
      <category>tools</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Detect Word Wrap in Textarea JavaScript</title>
      <dc:creator>ilyas Elaissi</dc:creator>
      <pubDate>Tue, 26 May 2026 10:47:05 +0000</pubDate>
      <link>https://dev.to/ilyas_elaissi/how-to-detect-word-wrap-in-textarea-javascript-knd</link>
      <guid>https://dev.to/ilyas_elaissi/how-to-detect-word-wrap-in-textarea-javascript-knd</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6yf3g22rgzptvd2vaib.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6yf3g22rgzptvd2vaib.png" alt="Illustration showing how to detect word wrap in textarea JavaScript using a hidden mirror div" width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A textarea will not tell you when its text wraps. There is no &lt;code&gt;wrap&lt;/code&gt; event, no callback, nothing. The browser draws the line break visually and moves on, and &lt;code&gt;textarea.value&lt;/code&gt; stays exactly the same string you typed. If you need to know when content has flowed onto a new visual row, you have to measure it yourself, and the cleanest way to do that is with a hidden mirror element. This guide on how to detect word wrap in textarea JavaScript walks through the exact technique I use in production, with a working example you can paste in.&lt;/p&gt;

&lt;p&gt;I have shipped this in two different rich-input components, and the trap most people fall into is trusting &lt;code&gt;scrollHeight&lt;/code&gt; on the textarea directly. It works until someone resizes the window, or until your CSS has a weird &lt;code&gt;padding&lt;/code&gt; value, and then your line counts drift by one. The mirror approach sidesteps all of that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why The Textarea Will Not Tell You Itself
&lt;/h2&gt;

&lt;p&gt;Word wrap inside an &lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt; is purely a rendering concern. The DOM value is the raw string the user typed. If they hit Enter, you get a &lt;code&gt;\n&lt;/code&gt;. If the browser soft-wraps a long sentence at column 47 because the box is 320 pixels wide, you get nothing. No newline, no event, no property flip.&lt;/p&gt;

&lt;p&gt;This is the hard versus soft newline distinction. A hard newline lives in &lt;code&gt;textarea.value&lt;/code&gt;. A soft wrap lives only in pixels. So the difference between what the user types and what the user sees, the value versus display problem, is the entire reason this is annoying to solve.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;input&lt;/code&gt; event fires on every keystroke, which is useful, but it fires whether wrapping happened or not. There is no built-in signal for the moment the line count changes. That gap between "text changed" and "visual layout changed" is the thing we have to bridge ourselves.&lt;/p&gt;

&lt;p&gt;You will see suggestions on Stack Overflow that divide &lt;code&gt;textarea.scrollHeight&lt;/code&gt; by &lt;code&gt;lineHeight&lt;/code&gt;. That works, sort of, but it depends on the textarea actually having room to grow, and it gets confused by min-height, padding rounding, and any CSS that touches the box. I have hit all three of those bugs. The mirror is more reliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Idea Behind The Hidden Mirror Element
&lt;/h2&gt;

&lt;p&gt;Build a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; that lies offscreen, give it the same width and font metrics as your textarea, dump the textarea's text into it, then read its height. Because the div uses the same wrapping rules, the same font, the same padding, and the same box-sizing, the browser will wrap the text the exact same way. Divide its height by the line height and you have your visual line count.&lt;/p&gt;

&lt;p&gt;That is the whole trick. A second DOM element shadows the first, the browser does the wrapping math for free, and you read the result.&lt;/p&gt;

&lt;p&gt;The reason this works is that text wrapping in CSS is deterministic given identical font metric matching, an identical content width, and the same &lt;code&gt;white-space&lt;/code&gt; and &lt;code&gt;overflow-wrap&lt;/code&gt; rules. Match those, and your mirror wraps where the textarea wraps. The &lt;code&gt;pre-wrap&lt;/code&gt; white-space rule is what makes the div respect both real newlines and soft wrapping at word boundaries, which is exactly what a textarea does by default.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why not Canvas 2D API or getClientRects
&lt;/h3&gt;

&lt;p&gt;You can measure text with the Canvas 2D API's &lt;code&gt;measureText&lt;/code&gt;, but you have to re-implement the wrapping algorithm yourself. Word breaks, Unicode segmentation, CJK rules, hyphens, all of it. Not worth it for this problem.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;getClientRects&lt;/code&gt; on a Range inside a textarea also does not work the way you might hope, because textareas use a shadow tree the page cannot reach. You cannot put a Range on the internal text node. So a parallel mirror div is the pragmatic answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Full Working Example
&lt;/h2&gt;

&lt;p&gt;Here is the complete solution. It listens for the &lt;code&gt;input&lt;/code&gt; event, recomputes the visual row count from the mirror, and updates a status line whenever the text wraps onto a new row.&lt;/p&gt;

&lt;p&gt;HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Type a long sentence..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Lines: 1&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;textarea&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;320px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;24px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.mirror&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;white-space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pre-wrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;word-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;break-word&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;break-word&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-9999px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-9999px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;textarea&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mirror&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mirror&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;previousLineCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;copyTextareaStyles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textarea&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getComputedStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textarea&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fontFamily&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fontFamily&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fontSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fontWeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fontWeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;letterSpacing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;letterSpacing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;padding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;border&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;border&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;boxSizing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;boxSizing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getVisualLineCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textarea&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;copyTextareaStyles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textarea&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;textarea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getComputedStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textarea&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lineHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineHeight&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollHeight&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;lineHeight&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;textarea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentLineCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getVisualLineCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textarea&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentLineCount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;previousLineCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Word wrap detected. Lines: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currentLineCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Lines: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currentLineCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;previousLineCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentLineCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;previousLineCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getVisualLineCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textarea&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type a long enough sentence and the status line will say "Word wrap detected." Hit Enter mid-sentence and it just increments the count. The same code handles both, because both produce more rows in the mirror.&lt;/p&gt;

&lt;h2&gt;
  
  
  Walking Through The Code
&lt;/h2&gt;

&lt;p&gt;Three pieces are doing work here: the style-copying function, the measurement function, and the two event listeners.&lt;/p&gt;

&lt;h3&gt;
  
  
  The style copy
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;copyTextareaStyles&lt;/code&gt; reads the computed styles off the textarea and applies them to the mirror. Computed style copying matters because shorthand CSS like &lt;code&gt;padding: 10px&lt;/code&gt; produces four resolved sub-properties, and &lt;code&gt;window.getComputedStyle&lt;/code&gt; returns the resolved values. Browsers are consistent about returning these as pixel strings, which is what we want.&lt;/p&gt;

&lt;p&gt;I am copying nine properties: width, font family, size, weight, line height, letter spacing, padding, border, and box-sizing. You may need to add &lt;code&gt;font-variant&lt;/code&gt;, &lt;code&gt;text-transform&lt;/code&gt;, or &lt;code&gt;word-spacing&lt;/code&gt; if your design uses them. The principle is simple: anything that affects how text lays out has to match.&lt;/p&gt;

&lt;p&gt;The width copy plus &lt;code&gt;box-sizing: border-box&lt;/code&gt; on both is what keeps the content area identical. If your textarea uses &lt;code&gt;border-box&lt;/code&gt; but your mirror falls back to &lt;code&gt;content-box&lt;/code&gt;, the mirror's text area will be slightly wider, and wrap points will drift by a character or two on long lines. Consistent box-sizing is non-negotiable.&lt;/p&gt;

&lt;h3&gt;
  
  
  The measurement
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;getVisualLineCount&lt;/code&gt; is three lines that matter. Copy the styles. Set the mirror's &lt;code&gt;textContent&lt;/code&gt; to the textarea value (with a fallback space so an empty string still gives height 1). Then divide the mirror's &lt;code&gt;scrollHeight&lt;/code&gt; by the parsed line height and round.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;scrollHeight&lt;/code&gt; works here because the mirror is not constrained by a min-height. It grows to fit its content exactly. The textarea, by contrast, often has a &lt;code&gt;min-height&lt;/code&gt; set, which is why measuring it directly is unreliable. This is the cleanest way I have found to calculate the visual line count from the rendered height.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Math.round&lt;/code&gt; matters. Browsers will sometimes return a &lt;code&gt;scrollHeight&lt;/code&gt; of 47.5 pixels when the line-height is 24, because of sub-pixel rounding in font metrics. Rounding lands you on the integer the human eye sees.&lt;/p&gt;

&lt;h3&gt;
  
  
  The two listeners
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;input&lt;/code&gt; listener handles every keystroke, paste, drag-drop, and IME composition commit. It is the right event for line detection tied to user typing. Compare the new count against the stored previous count, and you know whether wrapping just happened.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;resize&lt;/code&gt; listener handles the other half of the problem: the user has not typed anything, but the window got narrower and existing text wrapped onto more rows. We just refresh the baseline so the next keystroke does not falsely report a wrap event. If you want to react to wrap-on-resize too, this is the place to do it.&lt;/p&gt;

&lt;p&gt;For more aggressive cases I have swapped the window resize handler for a &lt;code&gt;ResizeObserver&lt;/code&gt; on the textarea itself. Resize observer handling is better when the textarea is inside a flex or grid container that can change width without the window changing size. The pattern is the same: recompute, store, move on.&lt;/p&gt;

&lt;p&gt;👉 Also read: &lt;a href="https://codetips.dev/ssr-vs-csr-vs-ssg-vs-isr" rel="noopener noreferrer"&gt;SSR vs CSR vs SSG vs ISR: Picking The Right Rendering Strategy&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Edge Cases That Will Bite You
&lt;/h2&gt;

&lt;p&gt;I have shipped this twice and both times something on this list broke me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom fonts that load late.&lt;/strong&gt; If your textarea uses a webfont, the first measurement runs against the fallback font, and your line counts will be wrong until the webfont swaps in. Fix it by re-measuring after &lt;code&gt;document.fonts.ready&lt;/code&gt; resolves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scrollbars.&lt;/strong&gt; If the textarea's height grows enough to need a vertical scrollbar, its content width shrinks by the scrollbar's width. The mirror, sitting offscreen, never has a scrollbar. Wrap points drift. The fix is to give the textarea &lt;code&gt;overflow-y: scroll&lt;/code&gt; permanently so the scrollbar's width is always factored in, or to subtract the scrollbar width from the mirror.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trailing newlines.&lt;/strong&gt; A textarea with the value &lt;code&gt;"hello\n"&lt;/code&gt; renders two rows. A div with &lt;code&gt;textContent = "hello\n"&lt;/code&gt; may render one, because trailing whitespace handling differs slightly. If this matters, append a marker character and subtract its row, or read the height before and after a trailing space.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Right-to-left text and CJK.&lt;/strong&gt; The mirror handles these correctly as long as &lt;code&gt;direction&lt;/code&gt; and &lt;code&gt;unicode-bidi&lt;/code&gt; are copied along with the other styles. I have not stress-tested mixed-direction content, so I would test that case specifically before relying on it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Composition events.&lt;/strong&gt; During IME composition (Japanese, Chinese, Korean input), &lt;code&gt;input&lt;/code&gt; events fire mid-composition with provisional text. The line count may bounce around as the candidate window updates. If that visible bouncing is annoying, debounce the listener to fire on &lt;code&gt;compositionend&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;For the spec-level details on the events involved, the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/input_event" rel="noopener noreferrer"&gt;MDN Web Docs page for the input event&lt;/a&gt; is the right reference. It is also a good place to confirm which events fire during paste and drop, which both trigger wrapping changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cheaper Alternatives And When They Are Fine
&lt;/h2&gt;

&lt;p&gt;The mirror is the most reliable option, but it is not always necessary. Three lighter approaches I have used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Divide textarea scrollHeight by lineHeight directly.&lt;/strong&gt; Works if your textarea has no &lt;code&gt;min-height&lt;/code&gt;, no padding rounding issues, and no scrollbar. About half the time, this is good enough. It also gives you a way to calculate the visual line count from &lt;code&gt;scrollHeight&lt;/code&gt; without the second DOM node.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Count &lt;code&gt;\n&lt;/code&gt; characters only.&lt;/strong&gt; If you only care about hard newlines and not soft wraps, &lt;code&gt;textarea.value.split("\n").length&lt;/code&gt; is the entire solution. Use this when "lines" means "what the user explicitly broke."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-grow with CSS only.&lt;/strong&gt; If your goal is just to make the textarea taller as the user types, &lt;code&gt;field-sizing: content&lt;/code&gt; (Chromium-only at the time of writing) does it natively. No JavaScript needed. Check browser support before relying on it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The mirror is what you want when you need an accurate count of rendered rows for things like virtual cursors, autocomplete positioning, or showing "your message will appear on N lines" to the user. For everything simpler, one of the above is less code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance Notes From Production
&lt;/h2&gt;

&lt;p&gt;A few things I have measured.&lt;/p&gt;

&lt;p&gt;Reading &lt;code&gt;window.getComputedStyle&lt;/code&gt; on every keystroke is fine. Modern browsers cache it, and it is a few microseconds at most. I tried memoizing it once and the code got uglier with no measurable win.&lt;/p&gt;

&lt;p&gt;Setting &lt;code&gt;mirror.textContent&lt;/code&gt; triggers a layout pass on the mirror, but because the mirror is &lt;code&gt;position: absolute&lt;/code&gt; and offscreen, it does not invalidate the rest of the page. The cost stays local.&lt;/p&gt;

&lt;p&gt;For very long textareas, think 5000+ character drafts, the mirror's layout starts to take a couple of milliseconds per keystroke. If you see input lag, debounce the measurement to roughly 16 ms with &lt;code&gt;requestAnimationFrame&lt;/code&gt;. Do not throttle to longer than a frame or the UI feedback feels broken.&lt;/p&gt;

&lt;p&gt;Do not put the mirror inside a CSS container that uses &lt;code&gt;contain: layout&lt;/code&gt; or &lt;code&gt;container-type&lt;/code&gt;. I lost an afternoon to that one. The wrapping behaved differently inside the contained context, and counts came out wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Tiny API Wrapper
&lt;/h2&gt;

&lt;p&gt;If you are reaching for this in more than one component, wrap it. Something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createLineCounter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textarea&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mirror&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;absolute&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;whiteSpace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pre-wrap&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;overflowWrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;break-word&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-9999px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-9999px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getComputedStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textarea&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;width&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fontFamily&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fontSize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fontWeight&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lineHeight&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;letterSpacing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;padding&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;border&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;boxSizing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;textarea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollHeight&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineHeight&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;destroy&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now any component can do &lt;code&gt;const counter = createLineCounter(ref.current)&lt;/code&gt; and call &lt;code&gt;counter.count()&lt;/code&gt; whenever it wants. Remember to call &lt;code&gt;destroy()&lt;/code&gt; on unmount so the mirror does not pile up in the DOM.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Would Do Differently Next Time
&lt;/h2&gt;

&lt;p&gt;Two things.&lt;/p&gt;

&lt;p&gt;First, I would put the mirror inside the same parent as the textarea, not on &lt;code&gt;document.body&lt;/code&gt;. Same stacking context, same inherited styles, less drift. The offscreen positioning still hides it. The only reason I used &lt;code&gt;body&lt;/code&gt; originally was that I was rushing.&lt;/p&gt;

&lt;p&gt;Second, I would treat the line count as derived state and only compare deltas. The example above stores &lt;code&gt;previousLineCount&lt;/code&gt; and updates the UI based on whether it grew. That is fine for a status message, but if you are driving real layout off this (like positioning an autocomplete popover), you want the absolute count every render, not a delta. Build the API to return the count and let the caller decide what to do with it.&lt;/p&gt;

&lt;p&gt;The mirror approach is older than most modern JS frameworks and it still beats everything else for this specific problem. Browsers have not added a wrap event in twenty years and probably never will, because the wrapping algorithm is part of layout and layout does not emit events. So this is the technique. Copy it, adapt it, ship it.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
