<?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: Byteminds Agency</title>
    <description>The latest articles on DEV Community by Byteminds Agency (@byteminds).</description>
    <link>https://dev.to/byteminds</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%2F1126106%2F70e97a8e-581a-4b78-91ac-addc5c210c72.jpg</url>
      <title>DEV Community: Byteminds Agency</title>
      <link>https://dev.to/byteminds</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/byteminds"/>
    <language>en</language>
    <item>
      <title>Starting as a Professional Frontend Developer</title>
      <dc:creator>Byteminds Agency</dc:creator>
      <pubDate>Fri, 06 Mar 2026 11:21:35 +0000</pubDate>
      <link>https://dev.to/byteminds/frontend-in-commercial-development-first-6-months-expectations-vs-reality-a49</link>
      <guid>https://dev.to/byteminds/frontend-in-commercial-development-first-6-months-expectations-vs-reality-a49</guid>
      <description>&lt;p&gt;Hey! I'm a frontend developer at ByteMinds. It's been six months since I joined the team and first encountered real production code.&lt;/p&gt;

&lt;p&gt;Before this, my experience consisted of pet projects and courses where everything was usually "sterile." In this article, I want to share not just impressions, but real experience of "surviving" in a commercial project. This is an honest story of what to be prepared for if you're looking for your first job.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack: Expectation vs Reality
&lt;/h2&gt;

&lt;p&gt;When I was studying, I thought projects picked one modern framework (like React) and everything was strictly built with it. In the "ideal world" of tutorials, that's exactly how it works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reality:&lt;/strong&gt; In commercial development, it's different. Right now I'm working on projects managed by CMS platforms (Umbraco and Optimizely). These systems are built on &lt;strong&gt;.NET&lt;/strong&gt;, where most of the frontend is rendered server-side through &lt;strong&gt;Razor templates (CSHTML)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this architecture, React isn't the "king" of the entire project. It's used selectively - as isolated components or blocks with complex logic that get embedded directly into Razor templates. The result is that on a single page, you might have several isolated React applications coexisting alongside markup that uses plain JavaScript scripts where needed. It turns out the ability to quickly switch between a framework's declarative approach and imperative vanilla JS is a crucial skill for "real combat."&lt;/p&gt;

&lt;p&gt;Typical project situation: a page renders through Razor, you're tweaking card markup and adding simple logic with vanilla JS, and on the same screen there's an isolated React component handling something complex - filtering, state management, async requests. Within a single task, you constantly have to switch between these approaches and choose the right tool for the specific problem, rather than trying to force everything into React for the sake of "architectural purity."&lt;/p&gt;

&lt;h2&gt;
  
  
  The Codebase: Not "Bad Code" but a History of Decisions
&lt;/h2&gt;

&lt;p&gt;A junior's first shock is legacy code. At first, it seems like the people who wrote this never heard of design patterns. But over time, understanding dawns: the codebase is a history. Some decisions were made in a rush before a release, others were driven by specific client requirements that changed three years ago.&lt;/p&gt;

&lt;p&gt;Navigating these layers of different eras is an art in itself. That's where modern tools and colleagues come in handy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI as a Co-pilot: My Experience with Cursor&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For me, Cursor has become an indispensable accelerator. I use it for pragmatic tasks where it's more efficient than manual searching:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Context and navigation:&lt;/strong&gt; It's great for understanding which files contain scattered logic and how components relate to each other in a massive project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Routine and boilerplate:&lt;/strong&gt; Generating TypeScript types for an API or scaffolding a component structure - tasks that AI handles like a pro.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Risk assessment:&lt;/strong&gt; Before refactoring, you can ask: "Where is THIS used, and what will break if I delete or change it?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Writing complex business logic? I wouldn't trust it with that yet, to be honest. AI is like an intern who works really fast but can confidently spout nonsense. So you still have to check every line of code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Colleagues&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Asking colleagues questions is one of the fastest ways to figure out a task.&lt;/strong&gt; They often have context that isn't in the code: why a particular solution was chosen, what was tried before, and where the hidden pitfalls are.&lt;/p&gt;

&lt;p&gt;These discussions not only save time but also help develop your "gut feeling." Gradually, you start to understand better where the real risks are and where you need to dig deeper, versus where you can accept the existing solution and move on.&lt;/p&gt;

&lt;p&gt;In commercial development, this is critical: it's not just about writing code, but doing it safely for the project. Talking with colleagues accelerates your onboarding and helps you start thinking in the context of the product, not just an individual task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design and Communication
&lt;/h2&gt;

&lt;p&gt;In pet projects, you're your own client. In commercial work, mockups are the foundation, but life throws curveballs. For example, a mockup shows 3 tags on a card, but the backend sends 7, and suddenly your layout starts to "dance."&lt;/p&gt;

&lt;p&gt;It's important to understand that design isn't always the ultimate truth. Often, designers themselves see their work more as an aesthetic direction rather than a strict final result. They don't always know 100% what real content and in what quantities will come from the backend.&lt;/p&gt;

&lt;p&gt;At that moment, responsibility falls on the developers. We're the ones who see the real data and have to decide how to display it as closely to the design as possible without breaking the UX. If in doubt, it's best to go to the designer and clarify their intent. But over time, you develop a feel for the boundaries of flexibility: where you can adapt the solution yourself, and where sign-off is crucial.&lt;/p&gt;

&lt;p&gt;This approach teaches you to be more than just "hands" - it teaches you to be an engineer who thinks about the user and the product, and tries to solve a problem before it surfaces in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sometimes You Have to Make Trade-offs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Things Don't Always Go to Plan: The Carousel Case&lt;/strong&gt;&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%2Fjvw0m3pcxspqdy8qkp9u.gif" 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%2Fjvw0m3pcxspqdy8qkp9u.gif" alt=" " width="600" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the most memorable moments was a task to implement a block that was a hybrid of a marquee and a regular slider. The designer kindly provided mockups with animation, approved by the client.&lt;/p&gt;

&lt;p&gt;The requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two rows of slides moving in the same direction, but at different speeds.&lt;/li&gt;
&lt;li&gt;Continuous movement (like a news ticker, linear flow), not slide-by-slide.&lt;/li&gt;
&lt;li&gt;Looped movement.&lt;/li&gt;
&lt;li&gt;Overall controls: autoplay on/off, next/previous slide.&lt;/li&gt;
&lt;li&gt;Slides are clickable cards.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Technical Struggle
&lt;/h2&gt;

&lt;p&gt;Initially, the project already used the Swiper library and had dozens of sliders implemented with it, so I decided not to add new dependencies. But, as it turned out, standard Swiper is designed for flipping through slides, not for linear flow (surprising, right?). To "squeeze" it to meet our needs, I had to search for hacks.&lt;/p&gt;

&lt;p&gt;I found a configuration that turns the slider into a marquee:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;typescript
const swiperDefaultOptions: SwiperOptions = {
  modules: [Autoplay, FreeMode],
  autoplay: {
    delay: 0,
  },
  freeMode: {
    enabled: true,
    momentum: false,
    momentumBounce: false,
  },
  loop: true,
  slidesPerView: "auto",
  spaceBetween: 24,
  speed: 4000,
};

// Initialization
this.swiperTopCarousel = new Swiper(this.refs.topSwiper, {
  ...swiperDefaultOptions,
  speed: Number(this.refs.topSwiper.dataset.speed) || swiperDefaultOptions.speed,
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And of course, a bit of CSS magic was needed. For smooth scrolling and predictable behaviour:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;css
.swiper-wrapper { transition-timing-function: linear; }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seemed to work fine, the hack solved everything, &lt;strong&gt;but&lt;/strong&gt;...&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%2F1nc4jjdi3y4tbk0rwrsj.gif" 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%2F1nc4jjdi3y4tbk0rwrsj.gif" alt=" " width="500" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Janky loop when dragging&lt;/strong&gt;. This became the main pain point. Because the slides had different widths, Swiper didn't calculate the "seam" point of cloned slides correctly. Visually, it looked like a jerk: the container would suddenly jump back to loop the animation. A possible fix would have been to calculate &lt;code&gt;slidesPerView precisely&lt;/code&gt;, but for the sake of responsiveness, we needed the auto value. The solution: we had to take away the user's ability to drag the slider. Harsh? Yes, but navigation buttons were still available.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stop on click&lt;/strong&gt;. Clicking a slide would stop Swiper's autoplay. I never fully figured out why. People online who'd implemented this hack suggested &lt;code&gt;disableOnInteraction: false&lt;/code&gt; and &lt;code&gt;pointer-events: none&lt;/code&gt; on the container. But that didn't work for us – the cards needed to be clickable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Solution: Compromise and a Second Library
&lt;/h2&gt;

&lt;p&gt;I realised I was trying to force Swiper to do something it wasn't designed for. The ideal candidate seemed to be Splide. It has a built-in &lt;code&gt;type: 'loop'&lt;/code&gt; mode with proper slide cloning, which solves the jerking issue. And the AutoScroll module smoothly moves the entire strip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;typescript
const baseOptions = {
  type: 'loop',
  autoWidth: true,
  gap: gap,
  arrows: false,
  pagination: false,
  drag: false, // disable drag, rely on autoscroll
  clones: clones
};

this.topSlider = new Splide(this.refs.topSlider, {
  ...baseOptions,
  autoScroll: {
    speed: 0.4,
    pauseOnHover: false,
    pauseOnFocus: false
  }
});

this.topSlider.mount({ AutoScroll });

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now came the dilemma: the project already had dozens of carousels using Swiper. Rewriting them all would be unjustifiably time-consuming and risky. Leaving the Swiper hack meant not delivering the task with the required quality.&lt;/p&gt;

&lt;p&gt;In the end, I made the tough call: to add Splide as a second library specifically for this case. Yes, it increases the bundle size. But in this situation, it was the only way to achieve truly smooth animation without writing a custom solution from scratch.&lt;/p&gt;

&lt;p&gt;When making such decisions, it's important to base them not just on code "beauty" and universality, but on the component's importance to the product. This carousel was the main visual feature of the case studies page and grabbed attention on first visit. Since the component directly impacted the first impression of the design, we decided not to compromise on the visuals.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lesson learned:&lt;/strong&gt; sometimes it's better to sacrifice bundle size for a solid UX and a stable solution, rather than maintaining fragile hacks in a critically important part of the interface.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  On Estimates and Responsibility
&lt;/h2&gt;

&lt;p&gt;Estimating tasks is pretty stressful at first. You allocate a certain amount of time, and you work towards it, but one unexpected carousel can eat up a good chunk of it due to unforeseen technical nuances.&lt;/p&gt;

&lt;p&gt;This taught me that an estimate isn't just a number the project should be ready for. It's always about planning for risks, even when it seems like there couldn't possibly be any. Now I always try to build in a buffer for researching existing code and unexpected circumstances.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Over these six months, I've understood the main thing: commercial frontend isn't just about writing code in the latest React. It's about the ability to work with what's already there, to negotiate, and to find a balance between "beautiful code" and a working business. It's harder than it seems in courses, but also more interesting and varied. In the end, I'd highlight these key takeaways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Proactivity matters more than knowledge&lt;/strong&gt;: If a task isn't clear - ask. It saves hours of wandering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ideal code is sometimes the enemy of the product&lt;/strong&gt;: In reality, you sometimes need to compromise to get a feature working stably and on time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Respect for Legacy&lt;/strong&gt;: Instead of criticising "crappy" code, focus on improving it safely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Soft skills are key&lt;/strong&gt;: Being able to explain your thoughts and accept feedback in code reviews makes you grow faster than just memorising syntax.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These were just the first six months. Real production is a noisy, non-linear thing, and school doesn't prepare you for it. But it's precisely in this chaos that you build the skills that make you a real developer.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Author: Yakov Shevcov, Frontend developer, ByteMinds&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Speeding Up Layout from Figma with MCP and Cursor</title>
      <dc:creator>Byteminds Agency</dc:creator>
      <pubDate>Fri, 13 Feb 2026 11:23:40 +0000</pubDate>
      <link>https://dev.to/byteminds/speeding-up-layout-from-figma-with-mcp-and-cursor-4211</link>
      <guid>https://dev.to/byteminds/speeding-up-layout-from-figma-with-mcp-and-cursor-4211</guid>
      <description>&lt;p&gt;Converting a design from Figma into code is an unavoidable stage of development, one that's often tedious and time-consuming. Even when we work with design systems where colors, fonts, and spacing are standardized, and layers and components are neatly named, transferring styles and double-checking every detail still eats up a lot of time and energy.&lt;/p&gt;

&lt;p&gt;If you're still stuck in the copy-paste loop and are thoroughly tired of this routine - it's high time to look towards AI and the useful tools it can offer. In this article, I'll show you how to leverage the full power of AI assistants in the design implementation process, using the duo of Cursor IDE and an MCP Server as an example.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://modelcontextprotocol.io/docs/getting-started/intro" rel="noopener noreferrer"&gt;MCP (Model Context Protocol)&lt;/a&gt; is a tool that burst onto the scene in 2025 and quickly found its place in the AI tool ecosystem. Its emergence was a logical step in the evolution of AI agents, which outgrew the limited context of a single model.&lt;br&gt;
MCP is an open-source standard that describes how AI agents and applications interact with each other and with external systems. Simply put, MCP acts as a language interpreter between products and Large Language Models (LLMs). It receives data in a specific service's "language" and translates it into a format the AI can understand.&lt;br&gt;
In other words, MCP allows AI agents to expand their capabilities by gaining access to a broader context. Through MCP, an AI can safely fetch data via API, search for information, perform analytics based on external services, and complete other tasks beyond the base model's reach.&lt;br&gt;
Companies like Figma can create their own MCP servers for their products by implementing this standard. Such a server knows how to call the product's API, extract the necessary context, and pass it to the agent in a structured way. In Figma's case, an MCP server directly integrates Figma into the workflow, providing AI agents with design information and the context needed to generate code based on Figma design files.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;Integrating such a tool into your workflow isn't complicated. You'll need: a code editor with MCP support (VS Code, Cursor, Claude Code, Codex, etc.) and access to a Figma project.&lt;/p&gt;

&lt;p&gt;Of course, there are nuances, such as design requirements, Figma's pricing plan, and other details. We'll discuss those a bit later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the Process Looks Like from a User's Perspective&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In broad strokes, the workflow is like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Setting up an MCP server inside your editor.&lt;/li&gt;
&lt;li&gt;Connecting to Figma via automatic browser authorization or manually using an access token (depending on the chosen MCP).&lt;/li&gt;
&lt;li&gt;Prompting the AI agent to implement the layout for the desired component.&lt;/li&gt;
&lt;li&gt;Reviewing the result, making edits, correcting the code.&lt;/li&gt;
&lt;li&gt;Clarifying requirements and expanding the context.&lt;/li&gt;
&lt;li&gt;Getting the result (rapid layout).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I've tried different MCPs in practice; they are all functional options with their own quirks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/grab/cursor-talk-to-figma-mcp" rel="noopener noreferrer"&gt;MCP Cursor Talk To Figma&lt;/a&gt; - a bit more demanding to set up (you need to set up a local server following the instructions and run the "Cursor Talk To Figma" plugin in Figma; works even without Dev Mode access).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/GLips/Figma-Context-MCP" rel="noopener noreferrer"&gt;Framelink MCP for Figma&lt;/a&gt; - convenient to set up, works even without Dev Mode access.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developers.figma.com/docs/figma-mcp-server" rel="noopener noreferrer"&gt;Figma MCP Server&lt;/a&gt; - official from Figma (but there's a nuance regarding Dev Mode).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's take a closer look at the process using the official Figma MCP Server as an example.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Process
&lt;/h2&gt;

&lt;p&gt;First, log into Figma and open your design project.&lt;br&gt;
Then, follow the &lt;a href="https://developers.figma.com/docs/figma-mcp-server/remote-server-installation/#cursor" rel="noopener noreferrer"&gt;installation instructions&lt;/a&gt; for the server in your IDE settings. It's genuinely simple and quick, taking just a couple of minutes. After installation and OAuth authorization, the server will show as 'enabled'.&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%2Fmq9slvyt5cayxu63qztq.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%2Fmq9slvyt5cayxu63qztq.png" alt=" " width="800" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the editor (in my case is Cursor) has access to a ready MCP server.&lt;br&gt;
Select the desired component in the design and copy its link:&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%2Fui0mbq3bgkpbi5ijxydg.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%2Fui0mbq3bgkpbi5ijxydg.png" alt=" " width="609" height="559"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Write your prompt. It can be concise:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Implement the component design from &lt;a href="https://www.figma.com/design/" rel="noopener noreferrer"&gt;https://www.figma.com/design/&lt;/a&gt;"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Or more detailed (this makes the agent's job easier and reduces the number of operations):&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"&lt;a class="mentioned-user" href="https://dev.to/feature"&gt;@feature&lt;/a&gt;.tsx Implement the component design from &lt;a href="https://www.figma.com/design/" rel="noopener noreferrer"&gt;https://www.figma.com/design/&lt;/a&gt; according to the &lt;a class="mentioned-user" href="https://dev.to/always"&gt;@always&lt;/a&gt;.md. Use Tailwind for layout, spacing and typography. Do not download images. Use placeholders from @images"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Executing such a prompt is quite fast - from 20 seconds for small components (card, quote, hero block) to a couple of minutes for an entire page.&lt;/p&gt;

&lt;p&gt;After that, the foundation is ready:&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%2Fmg3fcw18nylk6tlklqay.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%2Fmg3fcw18nylk6tlklqay.png" alt=" " width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"&lt;em&gt;- Replace custom Tailwind values with standard utility classes. For example, avoid arbitrary values like&lt;/em&gt; &lt;code&gt;p-[40px]&lt;/code&gt; &lt;em&gt;or&lt;/em&gt; &lt;code&gt;text-[64px]&lt;/code&gt;; &lt;em&gt;use default Tailwind spacing and font sizes&lt;/em&gt; (&lt;code&gt;p-10, text-4xl&lt;/code&gt;, etc.) &lt;em&gt;instead.- Replace custom CSS variable references in class names with design-system aliases. For example,&lt;/em&gt; &lt;code&gt;text-[var(--color-text-light-gray)]&lt;/code&gt; &lt;em&gt;should be written as&lt;/em&gt; &lt;code&gt;text-light-gray&lt;/code&gt;."&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%2Fxskn0s6jqnt0vrk6ztjd.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%2Fxskn0s6jqnt0vrk6ztjd.png" alt=" " width="800" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depending on the AI model and the complexity of the design and the existing codebase, the AI can produce different results. So, you need to help the agent with context. This is where rules come in - you can create and expand them as you see fit. The documentation offers some &lt;a href="https://developers.figma.com/docs/figma-mcp-server/add-custom-rules/" rel="noopener noreferrer"&gt;tips&lt;/a&gt; on this.&lt;/p&gt;

&lt;p&gt;The rules I used for a project with a React+Tailwind stack look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Cursor Project Rules &amp;amp; Guidelines
## Always Rules
## HTML / Accessibility (Component-Level Default)
- Use semantic tags (`section`, `article`, `nav`, `header`, `footer`) where relevant.  
- Follow proper heading hierarchy inside components (don’t start with `&amp;lt;h1&amp;gt;` unless it’s a full page).  
- Every image must have an `alt` attribute.  
- Make interactive elements (buttons, links, menus) keyboard accessible.  
## Code Generation &amp;amp; Architecture (JS/TS + React)
- Use **TypeScript** in strict mode; avoid `any`.  
- Prefer **type** over `interface` (except for classes).  
- Use **named exports only**; avoid default exports.  
- Prefer **guard expressions** instead of nested conditions.  
- Use **union types** instead of enums; `as const` objects are allowed.  
## CSS / Styling (General)
- Use Tailwind for styling.
- Use a **mobile-first** approach.  
- Prefer flexible dimensions (`min/max-width`) over fixed ones
## Color &amp;amp; Design System
- Extract exact colors from Figma data; do not approximate.
- If brand colors or repeated colors appear for the first time, create CSS variables in src/index.css.
- Use exact hex or rgba values from Figma; do not approximate with Tailwind.
- Font families and other constants should be defined in src/index.css.
- Use Tailwind responsive breakpoints for layout.
## Images and icons
- Do not attempt to download images, use a placeholder from `src/assets/images/`.  
- Always specify `alt`, `width`, and `height` attributes.  
- Use descriptive `alt` text based on the component’s purpose or content.  
## Development Workflow Optimization
- **Avoid redundant checks** - Don't check linter, existing configurations, or dependencies unless specifically requested
- **Skip obvious validations** - If package.json shows dependencies are installed, don't verify setup
- **Minimal todo lists** - Only create todos for complex multi-step tasks (3+ distinct steps)
- **Focus on core task** - Complete the main objective first, then add refinements if needed
- **Assume working environment** - Don't verify basic project setup unless errors occur

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's convenient to place the rules in a separate directory. For example, create a .cursor folder in the project root and put all rules inside a rules folder within it. Be sure to give the AI agent a link to the rules.&lt;br&gt;
Here's also an example of a detailed set of rules from another project configured with &lt;a href="https://github.com/GLips/Figma-Context-MCP" rel="noopener noreferrer"&gt;Framelink MCP&lt;/a&gt;:&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%2Fgc4bfccbo5x7brz1k3gw.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%2Fgc4bfccbo5x7brz1k3gw.png" alt=" " width="800" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's important to understand that the agent may selectively ignore formal rules. Sometimes, it makes sense to duplicate important requirements directly in the prompt. It doesn't hurt to add a reminder at the beginning of the dialogue like: "Respect all rules from .cursor/rules".&lt;/p&gt;

&lt;p&gt;Another topic is the AI agent's analysis of existing styles and components. Even if the agent correctly navigates the project structure and understands current UI patterns, it doesn't guarantee the generated code will fully meet your requirements and expectations.&lt;/p&gt;

&lt;p&gt;Here's a list of what positively affects the quality of the AI agent's output:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you're creating a project from scratch - set up the basic CSS settings for html, body, variables, media breakpoints, fonts, mixins, etc., yourself. On this foundation, the AI can help you pull missing colors, fonts, sizes, spacing from Figma into the project, and form styles for basic UI components, placing what you need into variables.&lt;/li&gt;
&lt;li&gt;Reference existing project files as a guide for structure, patterns, and code styles in TS/JS.&lt;/li&gt;
&lt;li&gt;Rules should be concise, clear, and consistent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fuller the context available to the AI agent, the more organically the design integration into code will proceed. Context includes not only rules and component examples but also basic project settings, existing files, styles, patterns, and any documentation that helps the agent correctly navigate the structure and development approaches.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can expand the context not only with rules and component examples. I highly recommend checking out Figma's Code Connect feature. It's only available for Organization and Enterprise plans, but it links real components from your repository with components in Figma - this way, the agent sees not only the design structure but also the actual code, import paths, props, and usage examples. If the Figma project is a true design system, such a tool seems like a must-have for a team planning to use MCP.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I also want to mention the topic of responsive design separately. AI handles generating the necessary layout based on provided responsive designs quite well. Example prompt:&lt;/p&gt;

&lt;p&gt;"&lt;a href="mailto:_@feature.tsx"&gt;_@feature.tsx&lt;/a&gt; Implement the component design from Figma:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mobile: &lt;a href="https://www.figma.com/design/" rel="noopener noreferrer"&gt;https://www.figma.com/design/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Desktop: &lt;a href="https://www.figma.com/design/" rel="noopener noreferrer"&gt;https://www.figma.com/design/&lt;/a&gt;
Ensure responsive design: switch to desktop layout at 1024px width._"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are a couple of visual examples for a project with only the most minimal basic settings so far, and a UI component base still being formed:&lt;br&gt;
Example of a website card:&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%2F4kb6yns5xad0o1y4wapb.jpg" 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%2F4kb6yns5xad0o1y4wapb.jpg" alt=" " width="784" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here's an entire page:&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%2Fa3o6lyvc6c9ouwwbyml5.jpg" 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%2Fa3o6lyvc6c9ouwwbyml5.jpg" alt=" " width="366" height="895"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, AI + MCP handle such tasks as well: it took me about a minute to formulate the request and generate the code. We got a result even with a raw, unconfigured project. However, this is more of a theoretical example. In practical development, this approach isn't justified. Converting an entire page at once creates an extra intermediary layer - code that will inevitably need to be redesigned, parsing and rethinking a large volume of already generated markup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nuances and Limitations
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Tokens&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You'll notice that executing requests consumes tokens, using up your editor's pricing plan limit. You can't avoid spending tokens altogether. But you can observe the dialogue with the AI to see what extra steps it's taking and then experiment with different models. For example, you can skip linter checks and intermediate code reports in the rules, etc.&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%2Ffxwj1mmr9gakpp105yyd.jpg" 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%2Ffxwj1mmr9gakpp105yyd.jpg" alt=" " width="800" height="601"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Real examples:&lt;br&gt;
Implementing a card component: 161.9k tokens – $0.10&lt;br&gt;
Implementing a home page (6 sections): 433.6k tokens – $0.21&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Figma API Limits&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After a couple of hours of work on a free plan, you'll likely get a "429 – too many requests" system message. This is a Figma API limitation, not MCP. As explained on &lt;a href="https://help.figma.com/hc/en-us/articles/34963238552855-What-if-I-m-rate-limited" rel="noopener noreferrer"&gt;Figma's site&lt;/a&gt;, this happens because applications and integrations are only allowed to send a certain number of requests to the REST API on your behalf. The limit depends on your pricing plan, license, and file location.&lt;/p&gt;

&lt;p&gt;In other words, using MCP on Figma's free plan will only work sporadically, a little, in test mode. Full-fledged work requires access to a paid plan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Dev Mode&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For the official Figma MCP, you need Dev Mode - a paid feature. Their site says the remote server is available for &lt;a href="https://help.figma.com/hc/en-us/articles/360040328273-Figma-plans-and-features" rel="noopener noreferrer"&gt;all seats and plans&lt;/a&gt;, and the desktop server is available for &lt;a href="https://help.figma.com/hc/en-us/articles/27468498501527-Updates-to-Figma-s-pricing-seats-and-billing-experience#h_01JCPBM8X2MBEXTABDM92HWZG4" rel="noopener noreferrer"&gt;Dev or Full seat&lt;/a&gt; on &lt;a href="https://help.figma.com/hc/en-us/articles/360040328273-Figma-plans-and-features" rel="noopener noreferrer"&gt;all paid plans&lt;/a&gt;. In practice, I only managed to connect to the remote MCP after activating Dev Mode access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Design Requirements&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With a messy design, you might get nowhere. Or rather, you might get something, but fixing the errors will take longer than if you did everything manually.&lt;/p&gt;

&lt;p&gt;The main recommendations for the design are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Layers should be grouped logically, their names should correspond to the context, and the correct structure of frames/components must be maintained.&lt;/li&gt;
&lt;li&gt;Layer and variable names should be clear, consistent, and reflect the element's role (e.g., color-primary, font-heading-lg, spacing-md).&lt;/li&gt;
&lt;li&gt;Auto-layout is used.&lt;/li&gt;
&lt;li&gt;Constraints are set for layers.&lt;/li&gt;
&lt;li&gt;Layers don't overlap.&lt;/li&gt;
&lt;li&gt;Alignment and spacing follow consistent rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When NOT to Use MCP&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I wouldn't use MCP when the design is poor - layers aren't grouped and are chaotic, colors and text styles are scattered and inconsistent, text is split into separate layers. Also, don't waste time converting designs for complex components, components with multi-layered elements and special animations. Or when the volume of work is small and targeted - in this case, it's probably easier to work with components manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we looked at what converting a design into code looks like with AI and MCP. Of course, refining components remains a separate process. The ready code proposed by the AI requires careful review, correction, and in some cases, a complete rewrite. Often, it's easier to just create a component manually from the start than to try to get a result automatically.&lt;/p&gt;

&lt;p&gt;And yet, it's impossible to deny that a properly configured MCP and correct interaction with it gives a powerful boost to frontend development. With skillful integration, the effort and time a developer spends on transferring and checking styles can be redirected towards architecture and logic. The main thing is not to rely blindly on the assistant. MCP is just an additional tool that helps speed up work, but the responsibility for code quality remains with the developer.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Author: Liya Vasilkova, frontend developer at ByteMinds&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>frontend</category>
      <category>cursor</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Setting Up a Frontend Build for HTML Email Templating with MJML</title>
      <dc:creator>Byteminds Agency</dc:creator>
      <pubDate>Thu, 18 Dec 2025 10:18:12 +0000</pubDate>
      <link>https://dev.to/byteminds/setting-up-a-frontend-build-for-html-email-templating-with-mjml-4eh1</link>
      <guid>https://dev.to/byteminds/setting-up-a-frontend-build-for-html-email-templating-with-mjml-4eh1</guid>
      <description>&lt;p&gt;In this article, we'll break down two key stages: first, we'll create a repository for email templating, and then we'll configure local test sending via SMTP.&lt;/p&gt;

&lt;p&gt;Why go through all this?&lt;/p&gt;

&lt;p&gt;On one of our projects, we actively use HTML emails. Initially, templates were placed directly in the backend microservices - scattered and with repetitive parts. This created difficulties in maintenance and email testing. We wanted to simplify the development process, and most importantly - make email templating and checking convenient.&lt;/p&gt;

&lt;p&gt;And as we know, email templating is painful. It's like you're developing for Internet Explorer - if you even remember what that is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving Emails to a Separate Repository
&lt;/h2&gt;

&lt;p&gt;We decided to create a separate repository where all email templates would be stored. The backend would be able to pull them centrally.&lt;/p&gt;

&lt;p&gt;The next step was choosing tools for templating and testing. We considered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MJML&lt;/li&gt;
&lt;li&gt;Maizzle&lt;/li&gt;
&lt;li&gt;Foundation HTML&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We wanted to write in a higher-level language, using pre-built components. With Maizzle and Foundation HTML, it seemed like we'd have to write more raw HTML code.&lt;/p&gt;

&lt;p&gt;In the end, we settled on MJML - a markup language for email templates that compiles into full-fledged HTML, adapted for the specifics of email clients.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is MJML and Why is it Convenient?
&lt;/h2&gt;

&lt;p&gt;MJML is a language designed for HTML email templating, which compiles into regular HTML. This means you can write in an abstract syntax that then turns into HTML code with support for different mail clients. This eases adaptation for various mail clients and gives the ability to use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;responsive templating;&lt;/li&gt;
&lt;li&gt;pre-built components;&lt;/li&gt;
&lt;li&gt;code reuse with mj-include.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see how MJML code turns into HTML &lt;a href="https://mjml.io/try-it-live" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Project Setup
&lt;/h2&gt;

&lt;p&gt;Let's go through the basic project setup - without delving into MJML syntax.&lt;/p&gt;

&lt;p&gt;Install dependencies:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install mjml live-server concurrently&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;What each one does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mjml&lt;/code&gt; - compilation MJML → HTML&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;live-server&lt;/code&gt; - starting a dev server with live reload (you can use any server)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;concurrently&lt;/code&gt; - running commands in parallel&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Configuring package.json
&lt;/h2&gt;

&lt;p&gt;Add to &lt;code&gt;scripts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {
  "start": "mjml --watch ./src/templates/**/*.mjml --output ./templates",
  "server": "live-server --host=localhost --watch=templates --open=templates --ignorePattern=\".*.mjml\"",
  "dev": "concurrently \"npm run start\" \"npm run server\"",
  "build": "mjml ./src/templates/**/*.mjml --output ./templates"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What each command does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;start&lt;/code&gt; - watches &lt;code&gt;*.mjml&lt;/code&gt; files in &lt;code&gt;src/templates/&lt;/code&gt;, compiles them to &lt;code&gt;./templates&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;server&lt;/code&gt; - starts a server and tracks changes in &lt;code&gt;./templates&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dev&lt;/code&gt; - runs &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;server&lt;/code&gt; in parallel&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;build&lt;/code&gt; - one-time template compilation without a watcher&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating the First Template
&lt;/h2&gt;

&lt;p&gt;Create a file &lt;code&gt;example.mjml&lt;/code&gt; at &lt;code&gt;/src/templates/example.mjml&lt;/code&gt;. All templates will be in the &lt;code&gt;/src/templates&lt;/code&gt; folder. Add the following code to the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      &amp;lt;/mj-column&amp;gt;
    &amp;lt;/mj-section&amp;gt;
    &amp;lt;mj-section background-color="#ffffff"&amp;gt;
      &amp;lt;mj-column&amp;gt;
        &amp;lt;mj-image src="https://placehold.jp/300x200.png" /&amp;gt;
      &amp;lt;/mj-column&amp;gt;
      &amp;lt;mj-column&amp;gt;
        &amp;lt;mj-text font-size="18px" font-weight="bold"&amp;gt;Hello, world!&amp;lt;/mj-text&amp;gt;
        &amp;lt;mj-text&amp;gt;This email was sent using MJML.&amp;lt;/mj-text&amp;gt;
      &amp;lt;/mj-column&amp;gt;
    &amp;lt;/mj-section&amp;gt;
    &amp;lt;mj-section&amp;gt;
      &amp;lt;mj-column&amp;gt;
        &amp;lt;mj-button href="#" background-color="#4CAF50"&amp;gt;Subscribe&amp;lt;/mj-button&amp;gt;
      &amp;lt;/mj-column&amp;gt;
    &amp;lt;/mj-section&amp;gt;
  &amp;lt;/mj-body&amp;gt;
&amp;lt;/mjml&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;npm run dev&lt;/code&gt; - and a browser will open with the HTML generated from MJML. Each time the template changes, the page will automatically update. It's also useful to create a &lt;code&gt;./src/parts&lt;/code&gt; folder for reusable layout components.&lt;/p&gt;

&lt;p&gt;As a result, we'll get desktop and mobile versions.&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%2Fil5oy5acgkb6scw6gqov.jpg" 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%2Fil5oy5acgkb6scw6gqov.jpg" alt="Desktop version" width="800" height="437"&gt;&lt;/a&gt;&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%2F38hbpl5yhg7hwjgchn5k.jpg" 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%2F38hbpl5yhg7hwjgchn5k.jpg" alt="Mobile version" width="654" height="795"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;./src/parts&lt;/code&gt; folder, create reusable email parts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;./src/parts/global-settings.mjml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;mj-style inline="inline"&amp;gt;
  body { background-color: #f0f4f6; font-family: Arial, sans-serif; }
  a { color: #1d5cdb; text-decoration: none; }
&amp;lt;/mj-style&amp;gt;
&amp;lt;mj-attributes&amp;gt;
  &amp;lt;mj-text
    font-size="17px"
    line-height="24px"
    color="#000"
    padding-top="5px"
    padding-bottom="5px"
  /&amp;gt;
&amp;lt;/mj-attributes&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;./src/parts/header.mjml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;mj-section&amp;gt;
  &amp;lt;mj-column&amp;gt;
    &amp;lt;mj-image
      src="https://placehold.jp/82x47.png"
      alt="Logo"
      width="82px"
      height="47px"
    /&amp;gt;
  &amp;lt;/mj-column&amp;gt;
&amp;lt;/mj-section&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's include them in example.mjml and remove unnecessary styles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;mjml&amp;gt;
  &amp;lt;mj-head&amp;gt;
    &amp;lt;mj-include path="../parts/global-settings.mjml" /&amp;gt;
    &amp;lt;mj-title&amp;gt;Example&amp;lt;/mj-title&amp;gt;
  &amp;lt;/mj-head&amp;gt;
  &amp;lt;mj-body&amp;gt;
    &amp;lt;mj-include path="../parts/header.mjml" /&amp;gt;
    &amp;lt;mj-section padding="40px 0 20px"&amp;gt;
      &amp;lt;mj-column&amp;gt;
        &amp;lt;mj-text align="center" font-size="28px" font-weight="bold"&amp;gt;Email built with MJML&amp;lt;/mj-text&amp;gt;
      &amp;lt;/mj-column&amp;gt;
    &amp;lt;/mj-section&amp;gt;
    &amp;lt;mj-section background-color="#ffffff"&amp;gt;
      &amp;lt;mj-column&amp;gt;
        &amp;lt;mj-image src="https://placehold.jp/300x200.png" /&amp;gt;
      &amp;lt;/mj-column&amp;gt;
      &amp;lt;mj-column&amp;gt;
        &amp;lt;mj-text font-size="18px" font-weight="bold"&amp;gt;Hello, world!&amp;lt;/mj-text&amp;gt;
        &amp;lt;mj-text&amp;gt;This email was sent using MJML.&amp;lt;/mj-text&amp;gt;
      &amp;lt;/mj-column&amp;gt;
    &amp;lt;/mj-section&amp;gt;
    &amp;lt;mj-section&amp;gt;
      &amp;lt;mj-column&amp;gt;
        &amp;lt;mj-button href="#" background-color="#4CAF50"&amp;gt;Subscribe&amp;lt;/mj-button&amp;gt;
      &amp;lt;/mj-column&amp;gt;
    &amp;lt;/mj-section&amp;gt;
  &amp;lt;/mj-body&amp;gt;
&amp;lt;/mjml&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We get the result:&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%2Fpyhd5tdw2njwhw07o559.jpg" 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%2Fpyhd5tdw2njwhw07o559.jpg" alt=" " width="800" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In summary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We can globally set common styles and settings for components using global-settings.&lt;/li&gt;
&lt;li&gt;We can similarly extract code parts, as done with header.&lt;/li&gt;
&lt;li&gt;We develop and see changes in the browser in real-time thanks to the configured dev server.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using Templates on the Backend (Go)
&lt;/h2&gt;

&lt;p&gt;Our project backend is written in Go. The compiled email templates end up in the &lt;code&gt;./templates&lt;/code&gt; folder. From there, the backend can pull them and send emails. In our case, connecting templates looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the project root - &lt;code&gt;go.mod&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module gitlab.site.ru/front-html-email-templates

go 1.22.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;In &lt;code&gt;./templates/templates.go&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package templates

import _ "embed"

//go:embed example.html

var Example string
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When adding a new template, a similar variable needs to be added to &lt;code&gt;templates.go&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Also, emails have variables that the backend substitutes using its templating engine. In our case, the syntax for variables is &lt;code&gt;{{.variableName}}&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;mjml&amp;gt;
  &amp;lt;mj-body&amp;gt;
    &amp;lt;mj-section padding="40px 0 20px"&amp;gt;
      &amp;lt;mj-column&amp;gt;
        &amp;lt;mj-text align="center" font-size="28px" font-weight="bold"&amp;gt;{{.title}}&amp;lt;/mj-text&amp;gt;
      &amp;lt;/mj-column&amp;gt;
    &amp;lt;/mj-section&amp;gt;
  &amp;lt;/mj-body&amp;gt;
&amp;lt;/mjml&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To understand what variables are needed in a template, we created a &lt;code&gt;./docs/templates&lt;/code&gt; folder where we store markdown files with the same name as the template and a description of which variables are used, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## Candidate

Email template 'Application without a vacancy'

### Variables
- `{{.date}}` Application Date
- `{{.title}}` Vacancy Title
- `{{.region}}` Region
- `{{.name}}` Full Name
- `{{.phone}}` Phone
- `{{.email}}` Email
- `{{.comment}}` Comment
- `{{.resume_link}}` Resume Link
- `{{.year}}` Current Year
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The variables themselves can be added to the files by the backend developer, and the frontend developer just needs to place them in the layout in the correct spots.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local SMTP Testing
&lt;/h2&gt;

&lt;p&gt;Let's write a simple JS script that will send our templates to a real email.&lt;/p&gt;

&lt;p&gt;First, install the following packages:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install dotenv nodemailer&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dotenv&lt;/code&gt; — package for loading .env files in JS;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nodemailer&lt;/code&gt; — package for sending emails with SMTP support.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After that, create a file &lt;code&gt;./send-test-email.js&lt;/code&gt; and add the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import nodemailer from 'nodemailer'
import dotenv from 'dotenv'
import fs from 'fs/promises'

dotenv.config()

async function sendTestEmail() {
  const transporter = nodemailer.createTransport({
    host: process.env.SMTP_HOST,
    port: process.env.SMTP_PORT,
    secure: true, // true for port 587, false for other ports
    auth: {
      user: process.env.SEND_FROM_EMAIL,
      pass: process.env.SEND_FROM_EMAIL_PASSWORD,
    },
  })gmail.com

  const htmlEmailString = await fs.readFile(
    `./templates/${process.env.TEMPLATE_NAME}.html`,
    'utf-8'
  )

  const mailOptions = {
    from: `Test Sender &amp;lt;${process.env.SEND_FROM_EMAIL}&amp;gt;`,
    to: process.env.SEND_TO_EMAIL,
    subject: 'Test HTML Email',
    html: htmlEmailString,
  }

  const info = await transporter.sendMail(mailOptions)
  console.log('Message sent: %s', info.messageId)
}

sendTestEmail().catch(console.error)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a simple implementation for sending emails via SMTP. We also need to create a .env file with the necessary variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TEMPLATE_NAME=email-confirmation
SEND_TO_EMAIL=test@byteminds.co.uk
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SEND_FROM_EMAIL=test@gmail.com
SEND_FROM_EMAIL_PASSWORD=BcsftTdfdsf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;TEMPLATE_NAME&lt;/code&gt; - the name of the template to be sent;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SEND_TO_EMAIL&lt;/code&gt; - where the email will be sent;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SMTP_HOST&lt;/code&gt; - SMTP host;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SMTP_PORT&lt;/code&gt; - port;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SEND_FROM_EMAIL&lt;/code&gt; - the sender's email address.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SEND_FROM_EMAIL_PASSWORD&lt;/code&gt; - the sender's email password.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Primarily, the &lt;code&gt;TEMPLATE_NAME&lt;/code&gt; and &lt;code&gt;SEND_TO_EMAIL&lt;/code&gt; variables change. For testing different templates and different mail clients.The other variables only need to be configured once.&lt;/p&gt;

&lt;p&gt;After all the setup, to send an email, you need to run the script:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;node ./send-test-email.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For convenience, you can add a script to package.json. You can also implement support for bulk sending a template to different mail clients.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The current concept can be applied with other tools as well. As a result, we have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a centralized repository for HTML emails;&lt;/li&gt;
&lt;li&gt;email templating with best practices and reusable parts;&lt;/li&gt;
&lt;li&gt;local SMTP testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's important to keep in mind that templating can still break in old Outlook clients and with solutions like MJML where standard templates are provided. Even using best practices doesn't completely solve this issue. To avoid "breaking" in old solutions, you can use very trivial layout: simple texts and headings, no styling or visual elements.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Author: Dmitry Berdnikov&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>gRPC for Testers: Quick Start After REST</title>
      <dc:creator>Byteminds Agency</dc:creator>
      <pubDate>Thu, 11 Dec 2025 12:05:39 +0000</pubDate>
      <link>https://dev.to/byteminds/grpc-for-testers-quick-start-after-rest-3pdg</link>
      <guid>https://dev.to/byteminds/grpc-for-testers-quick-start-after-rest-3pdg</guid>
      <description>&lt;p&gt;REST API has long been the standard, but it has its limitations. When load increases, streaming scenarios appear, or business requires faster communication between services, REST stops being the ultimate solution. gRPC solves what REST cannot: it provides high data transfer speeds, supports streaming, and enforces a strict interaction structure. It is convenient to use in microservices, mobile, and IoT applications.&lt;/p&gt;

&lt;p&gt;For a tester, this means one thing: knowing how to work with gRPC is shifting from a "nice-to-have" to an essential skill. If you haven't worked with this protocol yet, this article will help you understand its basics, how it differs from REST, the structure of .proto files, and most importantly - how to test gRPC services using Postman.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is gRPC and Why is it Needed?
&lt;/h2&gt;

&lt;p&gt;gRPC is a high-performance framework from Google. As a QA specialist, you don't necessarily need to dive into implementation details, but understanding how this protocol works and how to test it will definitely be useful.&lt;/p&gt;

&lt;p&gt;To simplify, REST can be compared to paper letters sent in envelopes, while gRPC is like a phone call. Both are formats for communication between an application and a server, but with REST API, data is transferred in JSON or XML formats, which in our analogy can be equated to envelopes. This is okay and works, but not always fast or convenient.&lt;/p&gt;

&lt;p&gt;gRPC proposes wrapping data in a binary protocol, which transmits data faster, has a smaller size, and is strictly structured. It uses Protocol Buffers (protobuf). Thanks to this, both sides participating in the information exchange know in advance what questions will be asked and what answers to expect. For a tester, this means less ambiguity and more predictability in verifications.&lt;/p&gt;

&lt;p&gt;When we first implemented gRPC in a project, it unexpectedly turned out that Postman, by default, couldn't work with this protocol. We had to find workarounds - we tried BloomRPC, then grpcurl. In the end, we found a solution: uploading .proto files into a new beta version of Postman. It was a useful lesson - tools aren't always ready "out of the box," and it's important for a tester to have several alternatives on hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Advantages of gRPC
&lt;/h2&gt;

&lt;p&gt;Why is gRPC usage increasingly common in product development? Here are the main reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High performance thanks to the binary protobuf format.&lt;/li&gt;
&lt;li&gt;Support for streaming: data can be transmitted not only in a "request-response" format but also as streams.&lt;/li&gt;
&lt;li&gt;Cross-platform: gRPC can be used for projects in different programming languages.&lt;/li&gt;
&lt;li&gt;Code autogeneration: client and server code is automatically generated from .proto files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this leads to fewer misunderstandings with developers, clear rules for validation, and new testing scenarios - for example, for streaming.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Differences Between gRPC and REST
&lt;/h2&gt;

&lt;p&gt;Moving from testing REST to gRPC implies conceptual changes in the process. Instead of endpoints and HTTP methods - methods and messages described in a contract. For convenience, let's compare them in a table.&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%2Ff0txwkildnvr4a08zluc.jpg" 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%2Ff0txwkildnvr4a08zluc.jpg" alt=" " width="606" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, let's look in more detail at one of gRPC's advantages over REST - the presence of streaming.&lt;/p&gt;

&lt;h2&gt;
  
  
  Types of gRPC Calls
&lt;/h2&gt;

&lt;p&gt;Unlike REST, which has the familiar GET/POST/PUT/DELETE, gRPC works with methods defined in a .proto file.&lt;/p&gt;

&lt;p&gt;Four types of calls are supported:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Unary&lt;/strong&gt; - one request, one response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server streaming&lt;/strong&gt; - one request, stream of responses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client streaming&lt;/strong&gt; - stream of requests, one response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bidirectional streaming&lt;/strong&gt; - stream of requests, stream of responses.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;When designing tests, it's important to consider that "one test - one request" isn't always suitable. For streaming, you need scenarios with multiple sequential messages.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But to understand which methods are available and what data can be transmitted in these calls, you need to understand how a .proto file is structured.&lt;/p&gt;

&lt;h2&gt;
  
  
  How a .proto File is Structured
&lt;/h2&gt;

&lt;p&gt;This is a simple text file with a .proto extension. In it, we essentially agree on what data can be sent and received, and what methods the service has.&lt;/p&gt;

&lt;p&gt;You can think of it as an instruction or contract between the service developer and those who use it (for example, QA specialists).&lt;/p&gt;

&lt;p&gt;Usually, the .proto file is created by the backend service developer, because they know which methods the service must support, as well as what data is accepted and returned. &lt;/p&gt;

&lt;p&gt;A tester can also open a .proto file to understand how to correctly form a request, suggest improvements (for example, add a field or change a data type), and, if desired, write their own .proto for learning or experiments.&lt;/p&gt;

&lt;p&gt;Before moving on to the structure of a .proto file, let's make an important clarification.&lt;/p&gt;

&lt;p&gt;gRPC indeed transmits data in the binary Protocol Buffers format, not JSON - this is why it is more efficient than REST in terms of speed and traffic volume. However, when testing via tools like Postman, BloomRPC, or grpcurl, you will see requests and responses in a human-readable format resembling JSON. This is a visualization of the binary data for humans. The tools automatically convert the binary stream into such "pseudo-JSON" to make it easier for you to work with the fields.&lt;/p&gt;

&lt;p&gt;In our examples, we will also use this readable format - not because gRPC uses JSON, but so you can quickly understand the message structure.&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%2F8jbkz0nmhzsa7sblq9pg.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%2F8jbkz0nmhzsa7sblq9pg.png" alt=" " width="800" height="572"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's look at a simple example of a .proto file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;syntax = "proto3";

service HelloService {

  rpc SayHello (HelloRequest) returns (HelloResponse);

}

message HelloRequest {

  string name = 1;

}

message HelloResponse {

  string message = 1;

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's Inside:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Syntax Specification&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;syntax = "proto3";&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Syntax&lt;/em&gt; indicates which version of the Protocol Buffers language we are using.&lt;br&gt;
At the time of writing, the current version is the third - proto3.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Service Definition&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;service HelloService {&lt;br&gt;
rpc SayHello (HelloRequest) returns (HelloResponse);&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;service HelloService&lt;/em&gt; - declaration of a service named HelloService.&lt;br&gt;
You can think of a service as a class containing methods.&lt;br&gt;
&lt;em&gt;rpc SayHello (...) returns (...)&lt;/em&gt;- this describes a remote procedure call, where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;SayHello&lt;/em&gt; — the method name.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;(HelloRequest)&lt;/em&gt; — what the method accepts (request type).&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;returns (HelloResponse)&lt;/em&gt; — what the method returns (response type).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our example, the service has only one method, &lt;em&gt;SayHello&lt;/em&gt;, which accepts a &lt;em&gt;HelloRequest&lt;/em&gt; message and returns a &lt;em&gt;HelloResponse&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request Message Description&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;message HelloRequest {&lt;br&gt;
string name = 1;&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Message &lt;em&gt;HelloRequest&lt;/em&gt; defines the data type for the request. Inside it:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;string name&lt;/em&gt; = 1; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;string&lt;/em&gt; - data type (string).&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;name&lt;/em&gt; - field name.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;= 1&lt;/em&gt; - sequence number (needed for serialisation).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means: the client must pass a string field name.&lt;/p&gt;

&lt;p&gt;Example: &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%2Fyr7ae5lpzegnzndnr29j.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%2Fyr7ae5lpzegnzndnr29j.png" alt=" " width="200" height="78"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Response Message Description&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;message HelloResponse {&lt;br&gt;
string message = 1;&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Message &lt;em&gt;HelloResponse&lt;/em&gt; defines the data type for the response. Inside:&lt;/p&gt;

&lt;p&gt;string message = 1; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a string that will contain the result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our case, the server will return a greeting string.&lt;/p&gt;

&lt;p&gt;Example:&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%2F2w9ar7i36gzn6ydg2ong.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%2F2w9ar7i36gzn6ydg2ong.png" alt=" " width="308" height="92"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All of this works as follows: the client calls the SayHello method, in the HelloRequest it sends the name field, the server receives this field, forms a response, and in the HelloResponse it returns a string with a greeting.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you know how to read and understand .proto files, you can easily grasp the data structure and suggest improvements to the API even at the design stage.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And now, it's logical to move on to the next question: what else does the service return besides useful data? Here we'll talk about gRPC status codes.&lt;/p&gt;

&lt;h2&gt;
  
  
  gRPC Status Codes: What a Tester Needs to Know
&lt;/h2&gt;

&lt;p&gt;In gRPC, every response (even a successful one) is accompanied by a status code. These are not HTTP 200/404/500, but their own set of codes defined in the gRPC library. In total, gRPC has 17 predefined status codes, from 0 to 16, but we will consider only the most basic and commonly used ones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Main gRPC Status Codes&lt;/strong&gt;&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%2F7edtxtjke7du34n7ktjd.jpg" 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%2F7edtxtjke7du34n7ktjd.jpg" alt=" " width="586" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It's important not only to verify the correctness of the data in the response but also to ensure the service correctly returns status codes in various scenarios.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  gRPC in Postman: Practical Testing
&lt;/h2&gt;

&lt;p&gt;Previously, Postman could only work with REST, but now - also with gRPC.&lt;/p&gt;

&lt;p&gt;How it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Upload the .proto file into Postman.&lt;/li&gt;
&lt;li&gt;Select the service and method.&lt;/li&gt;
&lt;li&gt;Form the request (data is taken from the request structure in the .proto).&lt;/li&gt;
&lt;li&gt;Look at the response and status code.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, for a simple service:&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%2F7cx8dikk3k59ikyjh22c.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%2F7cx8dikk3k59ikyjh22c.png" alt=" " width="526" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A request in Postman will contain the name field, and in response a greeting message will arrive.&lt;/p&gt;

&lt;p&gt;Let's look at an example.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a new request → select the request type gRPC Request (not HTTP!).&lt;/li&gt;
&lt;/ul&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%2Fazedb6lyprjgj0s02fj2.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%2Fazedb6lyprjgj0s02fj2.png" alt=" " width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Specify the URL, open the Service Definition tab, import the .proto file.&lt;/li&gt;
&lt;/ul&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%2F9dnxohyuhfizcy2lymel.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%2F9dnxohyuhfizcy2lymel.png" alt=" " width="800" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After importing the .proto file, all available methods the service can work with will become available for selection.&lt;/li&gt;
&lt;/ul&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%2Fg6nu8z3tiq6cbrvl5jvm.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%2Fg6nu8z3tiq6cbrvl5jvm.png" alt=" " width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select the method we need, and a JSON-like form with fields that need to be filled in will appear in the request body (all required fields are defined in our .proto file).&lt;/li&gt;
&lt;/ul&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%2F4auh11vfyvhm2gndnn37.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%2F4auh11vfyvhm2gndnn37.png" alt=" " width="722" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click Invoke and you will see the response and status code.&lt;/li&gt;
&lt;/ul&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%2F3yd0305syskp96uri7ph.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%2F3yd0305syskp96uri7ph.png" alt=" " width="86" height="43"&gt;&lt;/a&gt;&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%2F8p4dvivjx7f9tq4b85oc.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%2F8p4dvivjx7f9tq4b85oc.png" alt=" " width="722" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;gRPC is faster than REST, supports streaming, and enforces strict contracts.&lt;/li&gt;
&lt;li&gt;For a tester, the key skills are: being able to read .proto files, check different call types, and work with tools (Postman, grpcurl, BloomRPC).&lt;/li&gt;
&lt;li&gt;Don't forget about status codes: they help understand how a service behaves in error scenarios.&lt;/li&gt;
&lt;li&gt;In practice, you may encounter pitfalls: not every tool supports gRPC as conveniently as REST.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By mastering gRPC, you will expand your arsenal: you'll be able to test not only REST services but also more modern microservice systems. And that means - you'll become a more in-demand specialist.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Author: Vasil Khamidullin&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>qa</category>
    </item>
    <item>
      <title>Understanding CORS: A Practical Guide</title>
      <dc:creator>Byteminds Agency</dc:creator>
      <pubDate>Fri, 28 Nov 2025 11:30:36 +0000</pubDate>
      <link>https://dev.to/byteminds/understanding-cors-a-practical-guide-3lpm</link>
      <guid>https://dev.to/byteminds/understanding-cors-a-practical-guide-3lpm</guid>
      <description>&lt;p&gt;Have you ever seen a message in your console like: &lt;strong&gt;"Access to fetch at '...' from origin '...' has been blocked by CORS policy"&lt;/strong&gt;? CORS doesn't draw attention to itself when everything is working, but at a crucial moment, it firmly blocks unauthorized actions. For example, reading the response to a cross-origin request without the server's permission.&lt;/p&gt;

&lt;p&gt;Web technologies allow us to perform banking transactions instantly, make payments in online stores, collect and process data - but the more actively websites communicate with each other, the more pressing the issue of security becomes.&lt;/p&gt;

&lt;p&gt;CORS is a protection mechanism for cross-origin requests, but the first time I encountered this error, I didn't understand how to fix it. I tried adding the required header mentioned in the error message (we'll look at where to find these errors later), but that didn't help. I had to dig deeper: to understand what an origin is, how a simple request differs from a preflight request, why using a wildcard (&lt;code&gt;*&lt;/code&gt;) doesn't work with credentials, and where CORS ends and CSRF begins (don't worry too much about these terms now, they will be explained throughout the article).&lt;/p&gt;

&lt;p&gt;In this article, I will briefly answer questions about why the CORS policy was created, how it works, why a simple action like "setting a header on the backend" might not be enough, and what secure patterns to choose for the frontend.&lt;/p&gt;

&lt;p&gt;To understand the logic behind CORS, we need to figure out where this security policy started - namely, with the Same-Origin Policy (SOP): what it allows, what it forbids, and why CORS wouldn't be needed without it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is SOP and Origin
&lt;/h2&gt;

&lt;p&gt;It all started back in 1995 when everyone's beloved (or not so beloved) JavaScript appeared and its use was implemented on web pages. At that moment, the concept of a browser policy emerged, and it was called the Same-Origin Policy (SOP).&lt;/p&gt;

&lt;p&gt;SOP is a fundamental principle of browser security, guaranteeing that scripts from one Origin cannot access data from another origin without explicit permission. Initially, SOP only protected access to the DOM (page structure) of other origins, but it was later extended to other sensitive objects (like cookies and global JS objects).&lt;/p&gt;

&lt;p&gt;So, what is an Origin? An Origin (hereinafter referred to as Source) is a unique combination of scheme (protocol), domain, and port (see the diagram below). If at least one of these components differs, one Source will be different from another.&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%2Fl0b5vd35z4io0p9q6rs4.jpg" 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%2Fl0b5vd35z4io0p9q6rs4.jpg" alt="ORIGIN" width="800" height="193"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples of Matching or Non-Matching Origins
&lt;/h2&gt;

&lt;p&gt;The comparison table below provides examples:&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%2Fi8247ctxn375289jhc4c.jpg" 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%2Fi8247ctxn375289jhc4c.jpg" alt=" " width="663" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How SOP Works in Practice
&lt;/h2&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%2Fcm7uyhziij8kh50joofo.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%2Fcm7uyhziij8kh50joofo.png" alt=" " width="800" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's now break down a scenario step-by-step where the SOP policy might be triggered (Image above):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A user visits and requests resources located at &lt;code&gt;https://www.a.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The loaded resources from &lt;code&gt;https://www.a.com&lt;/code&gt; in the user's browser then initiate a request for resources located at &lt;code&gt;https://www.b.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The browser checks the so-called Origin and, as seen in this case, the origins do not match, so the browser blocks access to the resources from &lt;code&gt;https://www.b.com&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What Would Happen Without SOP
&lt;/h2&gt;

&lt;p&gt;Why was SOP necessary in the first place? What's the benefit for me as a user? And if I'm a web developer, why should I keep this in mind and work around the restrictions?&lt;/p&gt;

&lt;p&gt;It's hard to argue with the primary reason: &lt;strong&gt;&lt;em&gt;security&lt;/em&gt;&lt;/strong&gt;.&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%2Fk62kfhwpft10rv9ippvo.jpg" 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%2Fk62kfhwpft10rv9ippvo.jpg" alt="Without SOP" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's imagine SOP doesn't exist and play out a scenario (image above), disastrous for both the user (who becomes vulnerable) and the developer (whose product loses trust):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user logs into their bank at &lt;code&gt;bank.com&lt;/code&gt; (a session cookie is saved in the browser).&lt;/li&gt;
&lt;li&gt;They then visit a malicious website, &lt;code&gt;bad-site.com&lt;/code&gt;, which contains a hidden malicious script.&lt;/li&gt;
&lt;li&gt;This script initiates a request with the session cookies to bank.com on the user's behalf.&lt;/li&gt;
&lt;li&gt;As a result, the malicious script receives a response from the bank without your knowledge!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Without SOP, this script could get the user's recent transaction list, create a new transaction, etc. This is because, according to the original concept of the World Wide Web, browsers are obliged to add authentication data like session cookies and authorization headers at the platform level when making requests to the bank's site, based on that site's domain.&lt;/p&gt;

&lt;p&gt;Let's return to our reality, where a protection mechanism against such nuisances exists (image below). Steps 1-3 from the disastrous scenario would be the same, but at step 4, SOP would block access to the requested resources.&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%2Fx1crdmj9xbpcojc41a85.jpg" 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%2Fx1crdmj9xbpcojc41a85.jpg" alt="With SOP" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It was precisely to prevent such attacks that the Same-Origin Policy was introduced: browsers started automatically blocking scripts from one Origin from accessing data from another.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Important: Although JavaScript indeed doesn't have direct access to the bank session cookies, it can still send requests to the bank's website using those bank session cookies, as in the situations on images above.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Experienced readers might say you can simply set the &lt;code&gt;HttpOnly&lt;/code&gt; flag on cookies. However, this flag only became a standard in 2002. Others might mention &lt;code&gt;SameSite&lt;/code&gt;. But it only appeared in 2016 and became a standard only in 2019-2020.&lt;/p&gt;

&lt;p&gt;SOP restricts reading data from a foreign origin, but it does not block the sending of requests to foreign domains. The browser would still automatically include cookies for &lt;code&gt;bank.com&lt;/code&gt; when submitting a form to &lt;code&gt;bank.com&lt;/code&gt; - it's just that the script from bad-site.com wouldn't find out what the bank returned. SOP only prevents the attacker from reading the response and confirming the attack worked. To protect against such scenarios on the server side, additional measures are needed (e.g., CSRF tokens in forms, setting appropriate &lt;code&gt;SameSite&lt;/code&gt; and &lt;code&gt;HttpOnly&lt;/code&gt; values, etc.).&lt;/p&gt;

&lt;p&gt;This is the Same-Origin Policy (SOP) in the browser, designed to protect the user. It doesn't seem too complicated overall, right? The user doesn't need to think about it because the browser developers have already thought about it and implemented a protection mechanism. The developer also doesn't need to worry about it specifically in their code - it's enough to follow the established rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Restrictions Imposed by SOP
&lt;/h2&gt;

&lt;p&gt;So, SOP imposes a number of strict restrictions on interaction between resources from different Origins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Blocking Access to Page Content&lt;/strong&gt;. A script cannot read or modify the content of a page from another domain. For example, JavaScript from &lt;code&gt;site-a.com&lt;/code&gt; cannot access the DOM, cookies, &lt;code&gt;localStorage&lt;/code&gt;, or other data of a page on &lt;code&gt;site-b.com&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Frame Isolation&lt;/strong&gt;. If an &lt;code&gt;&amp;lt;iframe src="..."&amp;gt;&lt;/code&gt; from a foreign domain is embedded in a page, the parent script cannot access &lt;code&gt;iframe.contentWindow.document&lt;/code&gt; of that frame (and vice versa) access will be denied as long as their origins differ.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Blocking Access to HTTP Responses via XHR/Fetch.&lt;/strong&gt; The browser blocks receiving the response to AJAX requests sent by a script to another domain. That is, you can send a fetch to a third-party API, but if their Origins are different, the browser will not provide the response to the script.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Exceptions (Allowed Loads)&lt;/strong&gt;. SOP restricts access to data from third-party origins, not the loading of resources themselves. The browser can load images, styles, scripts, media, and frames from another domain without errors and use them as-is: display an image, apply a style, execute a script, play a video, render an iframe. However, the page's JavaScript is not allowed to read the internal content of these resources (image pixels, style rules, frame DOM, video bytes, etc.) unless the resource's server explicitly permits access via CORS.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Essentially, SOP says: "you can't read others' data," and this provides basic isolation. But the real web has long been multi-domain: we pull fonts from CDNs, call external APIs, facilitate communication between microservices. How to make such exchanges legitimate and secure without breaking isolation? This is where CORS comes onto the stage - a set of rules for coordinated access between different origins.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Emergence and Role of CORS
&lt;/h2&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%2Fkzskpq56q56vyq1nb4n4.jpg" 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%2Fkzskpq56q56vyq1nb4n4.jpg" alt="CORS" width="800" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SOP remains the foundation of browser security. But to allow controlled crossing of boundaries between origins, Cross-Origin Resource Sharing (CORS) was standardized: it adds explicit rules and headers to SOP, allowing the browser, based on server responses, to precisely grant access to clients from other Origins.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In essence, CORS is a browser technology that grants web pages access to resources from another domain under certain conditions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How the CORS Policy Works
&lt;/h2&gt;

&lt;p&gt;Let's say we have a frontend application on one domain (&lt;code&gt;https://www.a.com&lt;/code&gt;) that wants to request data from an API on another domain (&lt;code&gt;https://www.b.com&lt;/code&gt;). By default, SOP forbids the script from reading the response. However, the CORS standard defines a number of HTTP headers that the server (domain B) can use to tell the browser: "I trust domain A, you can let it read the response." This happens through embedded headers that the browser uses to regulate access between Origins.&lt;/p&gt;

&lt;p&gt;Now let's look at how CORS works in more detail and see which headers the browser relies on. When the browser makes an AJAX request (Fetch or XHR) to a third-party resource, it automatically adds an Origin header to the request, indicating the current origin of the page. For example, a request from the page &lt;code&gt;http://www.a.com/page.html&lt;/code&gt; to the resource &lt;code&gt;http://www.b.com/data.json&lt;/code&gt; would look like this (image below, step 2):&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GET /data.json HTTP/1.1 Host: www.b.com Origin: http://www.a.com&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The server &lt;code&gt;www.b.com&lt;/code&gt;, having received such a request, can decide to allow access. To do this, it must include the Access-Control-Allow-Origin header in the response, with a value of either the specific requesting domain-origin or * (the asterisk means "allow for any origin"). For example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Access-Control-Allow-Origin: http://www.a.com&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If the browser sees Access-Control-Allow-Origin in the response with the required origin (or *), it will not block the script's access to the received data. Otherwise if such a header is missing - blocking will occur: the JS code will get a network error instead of the data.&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%2F4iws9bqhfhp73krfk9ax.jpg" 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%2F4iws9bqhfhp73krfk9ax.jpg" alt="CORS Policy" width="800" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Besides the main permitting header, the CORS standard defines other access control headers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Access-Control-Allow-Credentials&lt;/code&gt;– controls access to resources considering authorization. If this header is set to true, the browser will allow access to such a response. Important: when using Allow-Credentials: true, the Allow-Origin value cannot be * - you must explicitly specify the specific domain, otherwise the browser will ignore the response.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Access-Control-Allow-Methods&lt;/code&gt; – a list of HTTP methods allowed when accessing the resource. If the script plans to send not only GET but, say, PUT or DELETE, the server must list them in this header, otherwise the browser will deny access.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Access-Control-Allow-Headers&lt;/code&gt; – similarly, a list of non-standard headers allowed in the request. For example, if the frontend wants to send a header like X-Custom-Header or Authorization, the server must explicitly allow them via this header.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Access-Control-Max-Age&lt;/code&gt; – the time (in seconds) for which the results of the preflight check can be cached. This header allows the browser to avoid making extra preflight checks (more on them later) for repeated requests within the specified time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Access-Control-Request-Method&lt;/code&gt; – a header sent in the preflight request (more on that later) informing the server of the intended method of the main request.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Access-Control-Request-Headers&lt;/code&gt; – a header sent in the preflight request (more on that later) informing the server of the list of non-standard headers the client wants to send in the main request.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But I want to note that requests can be different when viewed through the lens of CORS. This brings us to concepts like "simple" requests (see the image above) and "complex" ones (see the image below) (requiring a preliminary check via a preflight request).&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple and Complex Requests in CORS
&lt;/h2&gt;

&lt;p&gt;A simple CORS request is one that does not require an additional "handshake" with the server. The browser sends it immediately, only adding the Origin header, and expects a direct response with Access-Control-Allow-Origin.&lt;/p&gt;

&lt;p&gt;So how is a request determined to be complex? What rules does the browser rely on to determine the type of request? The standard defines characteristics for simple and complex requests.&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%2Fb1z2ab4btu5uu1gir6z6.jpg" 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%2Fb1z2ab4btu5uu1gir6z6.jpg" alt=" " width="608" height="770"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If all the requirements for a simple request are met, it is considered simple and the browser will send it directly. However, if just one condition is violated for example, specifying an &lt;code&gt;Authorization&lt;/code&gt; header for a token, or using the PUT method the browser will, before the main request, execute a special preliminary request (preflight) with the OPTIONS method to the same URL. This OPTIONS request does not contain a body but includes the headers &lt;code&gt;Access-Control-Request-Method&lt;/code&gt; (with the method of the main request) and &lt;code&gt;Access-Control-Request-Headers&lt;/code&gt; (a list of non-standard headers, if any).&lt;/p&gt;

&lt;p&gt;Thus, the browser asks the server if it allows a request with such parameters. The server must respond to the preflight request with a status of 200 (or 204) without a body, but with the previously mentioned headers: &lt;code&gt;Access-Control-Allow-Methods&lt;/code&gt; (listing allowed methods, e.g., PUT), &lt;code&gt;Access-Control-Allow-Headers&lt;/code&gt; (listing allowed non-standard headers, e.g., &lt;code&gt;Authorization&lt;/code&gt;, &lt;code&gt;X-Custom-Header&lt;/code&gt;), and the mandatory &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; (specifying the origin or *).&lt;/p&gt;

&lt;p&gt;If the browser receives a favourable response, it will proceed and execute the real request (e.g., PUT with the specified headers). And in response to the real request, the server must again include &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; (and, if needed, &lt;code&gt;Access-Control-Allow-Credentials&lt;/code&gt;) so that the browser delivers the data to the script.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Important note: This entire exchange happens automatically, without intervention from the front-end developer but if at any step the server doesn't return the necessary headers, the browser will reject the request.&lt;/p&gt;
&lt;/blockquote&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%2Fgqm15m26z73dersw1y13.jpeg" 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%2Fgqm15m26z73dersw1y13.jpeg" alt=" " width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  CORS Errors
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Developers can see CORS errors only through the browser console - JavaScript code, in case of policy violations, receives only a generic network error.&lt;/strong&gt; In the console, it will be indicated which header is missing or what exactly was blocked by the policy (Origin, method, header, etc.). To resolve the problem, you need to correctly configure the headers on the server.&lt;/p&gt;

&lt;p&gt;For example, errors of this nature can occur:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When requesting from origin &lt;code&gt;http://localhost:3000&lt;/code&gt; to another origin &lt;code&gt;http://localhost:4000&lt;/code&gt;, if the PUT method is not allowed, an error like this will appear in the console:&lt;/li&gt;
&lt;/ul&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%2Fv6f9gecref371y2sbh9p.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%2Fv6f9gecref371y2sbh9p.png" alt=" " width="767" height="57"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When requesting from origin &lt;code&gt;http://localhost:3000&lt;/code&gt; to another origin &lt;code&gt;http://localhost:4000&lt;/code&gt;, if the value of the Access-Control-Allow-Credentials header is not set to &lt;code&gt;true&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&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%2Fgibc7tt5yolvugt452br.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%2Fgibc7tt5yolvugt452br.png" alt=" " width="760" height="82"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When requesting from origin &lt;code&gt;http://localhost:3000&lt;/code&gt; to another origin &lt;code&gt;http://localhost:4000&lt;/code&gt;, if the request header custom-header is not allowed:&lt;/li&gt;
&lt;/ul&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%2Fhygv01cz0bp6crnt5hh4.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%2Fhygv01cz0bp6crnt5hh4.png" alt=" " width="760" height="69"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okay, we've seen how the browser decides "to allow or not." But what other methods of cross-origin interaction exist and why shouldn't they be confused with CORS? Let's discuss that next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternatives and Related Mechanisms
&lt;/h2&gt;

&lt;p&gt;Besides the discussed SOP and CORS, the following mechanisms can also be mentioned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bypassing SOP via document.domain.&lt;/strong&gt; Historically, a workaround was devised for subdomains: pages &lt;code&gt;aaa.example.com&lt;/code&gt; and &lt;code&gt;bbb.example.com&lt;/code&gt; could both execute a script assigning &lt;code&gt;document.domain = "example.com"&lt;/code&gt;, and then the browser would consider them the same origin. However, this approach is now outdated and declared unsafe. For example, Chrome plans to completely disable the ability to set document.domain because it undermines SOP protection (&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/domain#:~:text=Document%253A%2520domain%2520property%2520,and%2520complicates%2520the%2520origin" rel="noopener noreferrer"&gt;link to MDN&lt;/a&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Interaction via window.postMessage().&lt;/strong&gt; This API allows scripts from different origins to communicate safely. For example, a page from &lt;code&gt;domain-a.com&lt;/code&gt; can send a message to an embedded iframe from &lt;code&gt;domain-b.com&lt;/code&gt; by calling &lt;code&gt;iframe.contentWindow.postMessage&lt;/code&gt;(data, targetOrigin). If the targetOrigin matches (or "*" is specified for any), then on the domain-b.com side, the iframe will catch the message event and can read the data. An important property - neither the parent nor the iframe gains access to the other's DOM or JS objects; they only exchange string messages. postMessage is the primary way to integrate between different applications within the same window/tab (e.g., between a payment widget and a website).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;JSONP (JSON with Padding).&lt;/strong&gt; Before CORS became widespread, this was a popular trick for getting data from another domain. The gist: the site inserts a tag &lt;code&gt;&amp;lt;script src="https://other.com/data?callback=parser"&amp;gt;&lt;/code&gt; into the page. The server returns JavaScript code that calls the global function parser(...) with the JSON data inside. Because the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag is not blocked by SOP (the script will execute), the data "leaks" into the function call on the first site's side. Disadvantages of JSONP - it only works for GET requests and carries risks (execution of third-party code). Nowadays, JSONP is hardly used, having given way to CORS, which supports any methods and doesn't allow direct execution of foreign code.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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%2Fa7wxemu36nx2xwsoi9mv.jpg" 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%2Fa7wxemu36nx2xwsoi9mv.jpg" alt=" " width="800" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;WebSockets.&lt;/strong&gt; Interestingly, for WebSocket connections, SOP in its usual form does not apply. A page from JS can attempt to connect to &lt;code&gt;wss://another-domain.com/socket&lt;/code&gt; the browser will allow this. However: when establishing the WS connection, the browser still sends the Origin header in the handshake. The WebSocket server &lt;em&gt;&lt;strong&gt;must&lt;/strong&gt;&lt;/em&gt; check this header itself and decide whether to allow this origin. Otherwise, an attacker could bypass SOP and establish communication with a private server. Thus, security for WS is the responsibility of the server: the browser trusts it and does not block connection attempts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Resource Loading Control (CORP, COEP, COOP).&lt;/strong&gt; New standards introduce additional headers to enhance isolation. For example, Cross-Origin Resource Policy (CORP) allows a server to declare that its resources (scripts, images, etc.) should not be loaded on third-party sites. If an image with CORP=same-site is attempted to be inserted via &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; on a foreign site, the browser will block it entirely. This helps prevent side-channel attacks and information leakage through hidden resource inclusion. &lt;code&gt;Cross-Origin Opener/Embedder Policy&lt;/code&gt; (COOP/COEP) - even more advanced headers used for isolating contexts (e.g., to enable shared memory sharing, like &lt;code&gt;SharedArrayBuffer&lt;/code&gt;, between tabs of the same site, they must be completely isolated from outsiders). These topics are beyond the scope of this overview, but mentioning them shows how the idea of controlling interaction between sites is evolving.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Private Network Access (PNA).&lt;/strong&gt; Browser developers continue to enhance security policies. For example, in 2022, the &lt;code&gt;Private Network Access&lt;/code&gt; (PNA) mechanism appeared - an extension of CORS for protecting local networks. Chrome was one of the first to implement PNA: now if a script on a website from the internet tries to access a resource in a private network, before the actual request, the browser will send a special preliminary request with the header (&lt;a href="https://developer.chrome.com/blog/private-network-access-preflight" rel="noopener noreferrer"&gt;[Source link]&lt;/a&gt;):&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Access-Control-Request-Private-Network: true&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The local server (router) must respond with the header:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Access-Control-Allow-Private-Network: true&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;otherwise the browser blocks the connection. This measure aims to prevent attacks where attackers used the victim's browser for unauthorised access to devices on their local network.&lt;/p&gt;

&lt;p&gt;What exactly should we take away from all this? Next, we'll formulate key points and a minimal checklist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary and Practical Conclusions
&lt;/h2&gt;

&lt;p&gt;The Same-Origin Policy has been the foundation of web security for almost 30 years. Thanks to SOP, our browsers isolate tabs and frames from each other, preventing sites from stealing each other's data. At the same time, the modern web is impossible without the integration of different services and this is where CORS comes in handy. This mechanism carefully extends SOP, allowing safe data exchange between trusted domains. To work effectively with CORS, a developer needs to understand which headers to configure on the server and why the browser blocks a particular request. To summarize, let's note the key points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;SOP blocks scripts from accessing foreign content. Don't trust solutions that try to disable SOP - in modern browsers, this is impossible without compromising security.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CORS is a tool in the hands of the server-side developer. By correctly setting the headers (Origin, methods, headers, credentials), you tell the browser: "this request can be trusted," and the browser will comply.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When debugging CORS issues, carefully look at the messages in the browser console - they will hint at which header is missing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Always restrict access to the minimum necessary: specify concrete origins instead of *, allow only the necessary methods and headers. This reduces the chance of your API being abused.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Security is evolving: besides CORS, study other mechanisms (CSRF tokens, SameSite cookies, CSP, etc.) to build truly secure applications.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding SOP and CORS will allow you to work confidently with APIs, avoid annoying "Blocked by CORS" errors, and protect user data from most simple attacks on the web. This is mandatory knowledge for every web developer.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Author: Bair Ochirov&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cors</category>
      <category>sop</category>
    </item>
    <item>
      <title>How I Started Writing Unit Tests for Vue Components - Part 2</title>
      <dc:creator>Byteminds Agency</dc:creator>
      <pubDate>Fri, 21 Nov 2025 09:48:30 +0000</pubDate>
      <link>https://dev.to/byteminds/how-i-started-writing-unit-tests-for-vue-components-part-2-2cgj</link>
      <guid>https://dev.to/byteminds/how-i-started-writing-unit-tests-for-vue-components-part-2-2cgj</guid>
      <description>&lt;p&gt;So, it's been a year since the last &lt;a href="https://www.byteminds.co.uk/blog/how-i-started-writing-unit-tests-for-vue-components" rel="noopener noreferrer"&gt;article&lt;/a&gt;, and a lot has changed. In this one, we're going to talk about integrating with Mock Service Worker (MSW). I'll also describe what I tried to implement in my quest for system resilience - what worked out and what didn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, did my tests actually help me?
&lt;/h2&gt;

&lt;p&gt;I can't say the time investment paid off in spades, but one thing's for sure - it definitely wasn't a waste of time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here are the main areas where the tests really proved their worth:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When contracts were lost or changed;&lt;/li&gt;
&lt;li&gt;Fixing the fallout from merge conflicts (given the quirks of our processes, this is the most common scenario);&lt;/li&gt;
&lt;li&gt;Refactoring (it's hard to be objective here since our project's test coverage isn't huge, but before any refactoring, I try to at least cover the code with local tests).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then again, all those fancy things like Cursor with their powerful autocomplete freed up some time, so why not spend a bit of it on tests?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fight for Reliability, or Reality Strikes Back
&lt;/h2&gt;

&lt;p&gt;Let me start with what didn't work out.&lt;/p&gt;

&lt;p&gt;The first thing I tried was implementing E2E tests with Playwright. You know, testing business logic in the browser by simulating real user actions.&lt;/p&gt;

&lt;p&gt;In our existing project, this turned out to be really tough. The main problem was setting up the initial test data. In my case, that meant the database. It needed to be as small as possible, but still have all the necessary data for testing.&lt;/p&gt;

&lt;p&gt;In theory, it sounds simple: take a database, tweak the data, create a Docker image, and boom - you're golden. Well, I got stuck at the very first step of preparing that database. It requires a firm decision and a coordinated effort, meaning help from the backend team and DevOps (who are always busy). In the end, on my project, we shelved the idea for the time being.&lt;/p&gt;

&lt;p&gt;I also tried replacing actual backend interaction by mocking API requests directly within Playwright, but that felt like a dead end. Maintaining yet another set of mocks (on top of the MSW we already had) combined with the slow browser startup times just didn't seem rational, unless for some very specific tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  About Unit Tests and MSW
&lt;/h2&gt;

&lt;p&gt;All in all, I decided to focus on unit tests (which, in the classic sense, are more like integration tests in our context). They're fast, isolated, simple, and reliable.&lt;/p&gt;

&lt;p&gt;To mock network interactions, I set up MSW (Mock Service Worker). This later allowed us to practice contract-first programming and parallel development.&lt;/p&gt;

&lt;p&gt;So, first you install MSW (the &lt;a href="https://mswjs.io/docs/quick-start" rel="noopener noreferrer"&gt;official guide&lt;/a&gt; is your best friend here).&lt;/p&gt;

&lt;p&gt;Then, I moved the &lt;strong&gt;vite&lt;/strong&gt; configuration for it into &lt;strong&gt;vitest.workspace.js&lt;/strong&gt; (note: in new versions &lt;a href="https://vitest.dev/config/#projects" rel="noopener noreferrer"&gt;DEPRECATED&lt;/a&gt;). This isn't mandatory, but it's convenient if you need to separate node and browser environments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { defineWorkspace } from 'vitest/config';

export default defineWorkspace([
  'packages/*',
  {
    extends: './vite.config.js',
    test: {
      environment: 'jsdom',
      name: 'unit',
      include: ['src/**/*.spec.{ts,js}'],
      deps: {
        inline: ['element-plus'],
      },
      setupFiles: ['./src/mocks/setup.ts'], // path for the msw config
    },
  },
]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since it's an independent service, I put it in a mocks folder, so it's easy to cleanly remove if needed.&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%2F4pj6m9l0r1qms0jxwbv2.jpg" 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%2F4pj6m9l0r1qms0jxwbv2.jpg" alt="The structure" width="653" height="371"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { server } from './server.ts';
import { afterAll, afterEach, beforeAll } from 'vitest';

beforeAll(() =&amp;gt;  return server.listen({ onUnhandledRequest: 'warn' });
afterAll(() =&amp;gt; server.close());
afterEach(() =&amp;gt; server.resetHandlers());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;user/handlers.ts&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { GET_USERS } from '@/api/constants/APIEndPoints.js';
import { HttpResponse, http } from 'msw';
import { USERS_FAKE_RESPONSE} from './fixtures.ts';

export const handlers = [
  http.get('*' + GET_USERS , () =&amp;gt; {
    return HttpResponse.json(USERS_FAKE_RESPONSE);
  }),

];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, whenever a call is made to the URL defined in the &lt;strong&gt;GET_USER&lt;/strong&gt; identifier, it will return the value stored in &lt;strong&gt;USER_FAKE_RESPONSE&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Interestingly, MSW, especially with its &lt;a href="https://www.npmjs.com/package/openapi-msw" rel="noopener noreferrer"&gt;plugins&lt;/a&gt;, can generate handlers from an &lt;strong&gt;openApi.json&lt;/strong&gt; file, which can cover all your API requests. It can also use &lt;strong&gt;faker.js&lt;/strong&gt; to generate response data with fake values.&lt;/p&gt;

&lt;p&gt;I'm not a big fan of that approach myself (it can complicate parallel work), so I prefer to create response fixtures and handlers manually, and then fill them in - even using AI helpers sometimes - which results in more human-readable responses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const USER_FAKE_RESPONSE = {
  items:[
    { firstName: 'John' , lastName: 'Smith'}
    { firstName: 'Willy' , lastName: 'Willson'}
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using it in Tests
&lt;/h2&gt;

&lt;p&gt;For a clear example, let's imagine we have a component with a button to fetch users and a block to display the response. A traditional test might look something like this (a detailed test was in the previous article; this is just a schematic).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as USER_API from 'some api folder'
let wrapper

const createComponent = (params {}) =&amp;gt; {
  wrapper = shallowMount(OurGetUsersComponent, {
    props: {
      ...params.props,
    },
    global: {
      renderStubDefaultSlot: true,
      stubs: {
        ...params.stubs,
      },
    },
  });
};

test('Handling user retrieval when the Find button is clicked', async () =&amp;gt; {
const spyGetUsers =  vi.spyOn(USER_API, 'getUsersRequest').mockImplementation(() =&amp;gt;{  items:[
    { firstName: 'John' , lastName: 'Smith'}
    { firstName: 'Willy' , lastName: 'Willson'}
  ]}) 

  createComponent ()
  const buttonNode = wrapper.find('.button') //not a very good selector, but we only have 1 button
  await buttonNode.trigger('click');

  await flushPromises();
  expect(spyGetUsers).toHaveBeenCalled(); //here you can also check the parameters


  expect(wrapper.text()).toContain('Smith')
  expect(wrapper.text()).toContain('Willson')

});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That approach works, but what if we need to test the behavior when the server returns an error? For example, when a 500 error triggers a toast notification saying, "The server is temporarily unavailable, please try again later."&lt;/p&gt;

&lt;p&gt;This is exactly where MSW comes to the rescue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { server } from '@/mocks/server';
import { http, HttpResponse } from 'msw';
import { USER_FAKE_RESPONSE } from '...fixtures'
import * as MESSAGE_MODULE from "utils"
import { GET_USERS } from '@/api/constants/APIEndPoints.js';

let wrapper

const createComponent = (params {}) =&amp;gt; {
  wrapper = shallowMount(OurGetUsersComponent, {
    props: {
      ...params.props,
    },
    global: {
      renderStubDefaultSlot: true,
      stubs: {
        ...params.stubs,
      },
    },
  });
};

test('Handling user retrieval when the search button is clicked', async () =&amp;gt; {
const spyGetUsers =  vi.spyOn(USER_API, 'getUsersRequest') // the implementation already exists in MSW and doesn't need to be duplicated here

  createComponent ()
  // it's better to search the same way as the user  
  const buttonNode = wrapper.findAll('.button').filter(item=&amp;gt;item.text()=="Search")[0] 
  await buttonNode.trigger('click');

  await flushPromises();
  expect(spyGetUsers).toHaveBeenCalled(); // This step might be redundant since the result is what matters to the user

  expect(wrapper.text()).toContain(USER_FAKE_RESPONSE.items[0].lastName)
  expect(wrapper.text()).toContain(USER_FAKE_RESPONSE.items[1].lastName)

});

test('Handling server errors when retrieving users', async ()=&amp;gt;{
spyMessage = vi.spyOn(MESSAGE_MODULE , 'showErrorMessage')

  server.use(
    http.get('*' + GET_USERS, () =&amp;gt; {
      return new HttpResponse(null, { status: 500 });
    }),
  );
  createComponent ()
  const buttonNode = wrapper.findAll('.button').filter(item=&amp;gt;item.text()=="Search")[0] 
  await buttonNode.trigger('click');

expect(spyMessage ).toHaveBeenCalledWith({message: 'The server is temporarily unavailable, please try again later' });

})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, you can make your unit tests a little more honest and your team's capabilities a little broader.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Author: Dmitry Simonov&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>vue</category>
    </item>
    <item>
      <title>Performance Issues in Web Services: A Practical Guide to Identification and Resolution</title>
      <dc:creator>Byteminds Agency</dc:creator>
      <pubDate>Fri, 31 Oct 2025 12:25:31 +0000</pubDate>
      <link>https://dev.to/byteminds/performance-issues-in-web-services-a-practical-guide-to-identification-and-resolution-226g</link>
      <guid>https://dev.to/byteminds/performance-issues-in-web-services-a-practical-guide-to-identification-and-resolution-226g</guid>
      <description>&lt;p&gt;In the realm of web development, performance is frequently treated as an afterthought—a quality to be optimised after core functionality is delivered. This approach, however, is fundamentally flawed. Performance is not a feature; it is a foundational requirement, as critical as security, accessibility, or functional correctness. Clients may not explicitly demand a response time of ‘X milliseconds,’ but they will undoubtedly express dissatisfaction if a site feels sluggish or unresponsive. This guide provides a structured approach to integrating performance thinking into the development lifecycle, ensuring that web services are robust, efficient, and scalable from the outset.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Integrating Performance from the Initial Stages
&lt;/h2&gt;

&lt;p&gt;The most effective method for ensuring high performance is to consider it during the initial architecture and design phases. Proactive planning is exponentially more effective than reactive optimisation. When performance is an afterthought, developers often encounter deeply embedded inefficiencies that are costly and time-consuming to rectify. Common issues include components that generate excessive database queries, a lack of caching strategy, and architecture that cannot scale under load.&lt;/p&gt;

&lt;p&gt;Treating performance as a core skill involves cultivating a mindset of constant vigilance. Developers should not engage in premature optimisation but should instead learn to identify potential bottlenecks during the design and implementation phases. This includes questioning the necessity of each database call, considering the weight of third-party scripts, and planning for caching strategies before writing the first line of code.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Defining Clear Performance Objectives
&lt;/h2&gt;

&lt;p&gt;Vague expectations like “it should be fast” are insufficient for guiding development. Teams must establish clear, measurable performance goals. For front-end performance, metrics such as &lt;strong&gt;Largest Contentful Paint (LCP)&lt;/strong&gt;, &lt;strong&gt;Interaction to Next Paint (INP)&lt;/strong&gt; and &lt;strong&gt;Cumulative Layout Shift (CLS)&lt;/strong&gt; (collectively known as Core Web Vitals) provide a user-centric benchmark for quality.&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%2F6qkxn2marc8mt39zgncz.jpg" 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%2F6qkxn2marc8mt39zgncz.jpg" alt="Core Web Vitals" width="501" height="1251"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For server-side performance, a widely accepted standard is that the backend should deliver HTML content in &lt;strong&gt;under 200 milliseconds&lt;/strong&gt; for the vast majority of requests, even under typical load. This target allows sufficient time for the browser to download assets, render the page, and become interactive without frustrating the user. Establishing these benchmarks early creates a shared understanding with clients and provides a clear target for the development team.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Estimating and Planning for Load
&lt;/h2&gt;

&lt;p&gt;A critical yet often overlooked step is estimating the expected traffic a service must handle. For existing businesses, historical data from analytics platforms (e.g., Google Analytics) is invaluable. Key metrics to analyse include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The busiest month and day in terms of traffic.&lt;/li&gt;
&lt;li&gt;The peak hour of activity.&lt;/li&gt;
&lt;li&gt;The number of concurrent users during these peaks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For new projects without historical data, estimation requires a dialogue with the client. Essential questions include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who is the target audience?&lt;/li&gt;
&lt;li&gt;What is the expected number of daily and monthly active users?&lt;/li&gt;
&lt;li&gt;What are the conversion goals (e.g., sales, sign-ups)?&lt;/li&gt;
&lt;li&gt;Are there anticipated periods of high traffic, such as seasonal promotions or marketing campaigns?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these numbers, you can build a simple chain: if the site needs to generate 10,000 enquiries per month, and the conversion rate, according to the client, is 1%, then you need about 1,000,000 sessions per month. Dividing this by days and estimating the average duration of one session is enough to roughly understand how many people might be on the site simultaneously.&lt;/p&gt;

&lt;p&gt;For example, if there are ~33,000 sessions per day, and each lasts an average of 1 minute, then at any given time, approximately 20–30 people could be on the site concurrently. These are concurrent users—the number of sessions happening in parallel.&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%2F3uartvczrenhqyynoucp.jpg" 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%2F3uartvczrenhqyynoucp.jpg" alt=" " width="800" height="831"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's nothing complicated about this model. But if you don't build it at all, it will be difficult to explain later why the site went down during an advertising campaign or crashed on launch day. Crucially, these assumptions must be documented and discussed with the client to ensure alignment and manage expectations.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Proactive Identification of Bottlenecks
&lt;/h2&gt;

&lt;p&gt;Waiting for a quality assurance team to discover performance issues is a high-risk strategy. Developers should adopt tools and practices for self-testing during development.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Local Profiling:&lt;/strong&gt; Simple checks during development can reveal significant problems, such as pages taking tens of seconds to load or components making redundant API calls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load Testing Tools:&lt;/strong&gt; Frameworks like K6, Gatling, or Locust allow developers to simulate virtual users and measure a system’s behaviour under load. Writing a basic test script is straightforward and can be accomplished quickly, often with the help of AI assistants.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Synthetic Monitoring:&lt;/strong&gt; Running a script to crawl a site’s main pages (via its sitemap) on a “cold” startup can uncover issues with slow database queries or missing caches before they impact real users.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not to become a performance engineer but to develop a habit of verifying that one’s code behaves efficiently under expected conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Common Sources of Performance Issues
&lt;/h2&gt;

&lt;p&gt;Performance bottlenecks often originate in common, site-wide components. Identifying these early is key:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Global Components (Header, Footer, Navigation):&lt;/strong&gt; Fetching this rarely changing data from a CMS or database on every page request is inefficient. This content should be heavily cached or statically generated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Elements (Breadcrumbs, User Data):&lt;/strong&gt; Building breadcrumb trails through sequential database queries or rendering user-specific data (e.g., cart items) on the server can cripple performance. These should be optimised with pre-computation or moved to the client side where possible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search and Filtering&lt;/strong&gt;: Implementing complex filters and search functionality with direct SQL queries is a common mistake. This is a solved problem; dedicated search engines like &lt;strong&gt;Elasticsearch&lt;/strong&gt; or &lt;strong&gt;Algolia&lt;/strong&gt; are designed for this purpose and offer superior performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metadata and Redirects:&lt;/strong&gt; Generating meta tags (e.g., for multi-language support) and processing large redirect lists on every request adds significant overhead that can be mitigated through caching.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asset Optimisation:&lt;/strong&gt; Serving unoptimised images or processing them “on-the-fly” consumes substantial resources. Images should be optimised, resized, and served in modern formats (WebP, AVIF) as part of the build process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these things are simply a consequence of no one thinking during implementation about how the component would behave under load. And later, it becomes a common problem. But almost always, the reason is the same: an element that should have been light turned out to be heavy, and it wasn't cached. And since it appears on many pages, everything starts to slow down. &lt;/p&gt;

&lt;h2&gt;
  
  
  6. Implementing a Strategic Caching Layer
&lt;/h2&gt;

&lt;p&gt;Caching is the most powerful tool for improving performance and should be architectured into an application, not bolted on later.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CDN (Content Delivery Network):&lt;/strong&gt; Used to cache static assets (JS, CSS, images) and even entire HTML pages at geographically distributed edge servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output Cache:&lt;/strong&gt; Caches the fully rendered HTML output of a page, allowing the server to bypass the entire application logic for subsequent requests. This is highly effective for pages that are the same for many users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data-Layer Cache:&lt;/strong&gt; Stores the results of expensive database or API queries in a fast in-memory data store (like Redis), preventing repeated calls to the primary database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A well-designed caching strategy dramatically reduces server load, decreases response times, and improves scalability. While cache invalidation can be complex, most modern web frameworks provide robust tools to manage it effectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Fostering a Culture of Performance
&lt;/h2&gt;

&lt;p&gt;Ultimately, ensuring performance is not the sole responsibility of a team lead or architect; it is a collective responsibility shared by every developer. Cultivating a “performance mindset” involves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Education:&lt;/strong&gt; Ensuring all team members understand common pitfalls and best practices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tooling:&lt;/strong&gt; Providing integrated tools for profiling and testing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accountability:&lt;/strong&gt; Encouraging developers to take ownership of their code’s efficiency by asking simple questions before committing: “Are there unnecessary queries?” “Can this be cached?” “How will this behave with 100 concurrent users?”&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building high-performance web services is an achievable goal that requires a deliberate and proactive approach. By integrating performance considerations from the very beginning, defining clear metrics, estimating load, proactively testing for bottlenecks, and implementing a strategic caching layer, teams can deliver experiences that are not only functional but also fast and reliable. This shift from reactive fixing to proactive building is what separates adequate services from exceptional ones. In today’s competitive digital landscape, performance is not just a technical concern—it is a fundamental business imperative.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Author: Dmitry Bastron&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>web</category>
    </item>
    <item>
      <title>Setting SMART Goals for Digital Product Success</title>
      <dc:creator>Byteminds Agency</dc:creator>
      <pubDate>Fri, 27 Jun 2025 07:41:26 +0000</pubDate>
      <link>https://dev.to/byteminds/setting-smart-goals-for-digital-product-success-3ag7</link>
      <guid>https://dev.to/byteminds/setting-smart-goals-for-digital-product-success-3ag7</guid>
      <description>&lt;p&gt;In digital product development, vague aspirations such as "improve user experience" or "increase engagement" often lead to wheel-spinning rather than progress. This is where SMART goals transform abstract ideas into actionable, results-driven objectives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why SMART Matters in Digital Product Development
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;SMART — Specific, Measurable, Achievable, Relevant, and Time-bound — is a framework that clarifies goal-setting.&lt;/strong&gt; For product teams, it’s not just a project management framework, it’s a way to align daily tasks with strategic outcomes.&lt;/p&gt;

&lt;p&gt;Each component of SMART helps teams focus clearly on their tasks:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Specific&lt;/strong&gt;&lt;br&gt;
Instead of: "Make the app faster."&lt;br&gt;
SMART version: "Reduce app load time from 4 seconds to under 2 seconds for 95% of users."&lt;br&gt;
Why it works:  Eliminates ambiguity so developers, designers, and stakeholders share the same understanding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Measurable&lt;/strong&gt;&lt;br&gt;
Instead of: "Improve onboarding."&lt;br&gt;
SMART version: "Increase Day-1 retention from 40% to 60% by streamlining the signup flow."&lt;br&gt;
Why it works: Metrics provide a clear benchmark for success.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Achievable&lt;/strong&gt;&lt;br&gt;
Instead of: "Rebuild the entire backend in one sprint."&lt;br&gt;
SMART version: "Migrate the payment module to a more scalable architecture within three sprints."&lt;br&gt;
Why it works: Balances ambition with realistic resource allocation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Relevant&lt;/strong&gt;&lt;br&gt;
Instead of: "Add social media integrations."&lt;br&gt;
SMART version: "Implement Twitter/X login to reduce signup friction for our target B2B users."&lt;br&gt;
Why it works: Ensures every task relates to business or user needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time-bound&lt;/strong&gt;&lt;br&gt;
Instead of: "Redesign the dashboard."&lt;br&gt;
SMART version: "Launch the new dashboard UI by Q3 to support upcoming enterprise feature releases."&lt;br&gt;
Why it works: Deadlines create urgency and prevent scope creep.&lt;/p&gt;

&lt;p&gt;Using SMART goals minimizes misunderstandings, increases transparency, and aligns tasks strategic objectives.&lt;/p&gt;

&lt;h2&gt;
  
  
  SMART Goals Throughout the Digital Product Lifecycle
&lt;/h2&gt;

&lt;p&gt;From initial concept to mature product, SMART goals provide structure and alignment across all phases — from discovery to scaling and post-release.&lt;/p&gt;

&lt;p&gt;Early in development, it is important to set benchmarks that will help identify real user needs and define the direction of development. For example, during  discovery, you can formulate a goal: “Collect 30 user interviews within three weeks to confirm the hypothesis about the need for a new feature.”&lt;/p&gt;

&lt;p&gt;Impact Mapping and User Story Mapping will also help conduct a high-quality Discovery phase for a digital product. In combination with SMART goals, these practices will help connect goals with real user scenarios and form a solid foundation for the product roadmap.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For MVP and launch phase, goals may look like this: “Achieve 1,000 registrations in the first month after release” — this is a specific, measurable, and achievable goal with a business-oriented result.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When scaling or optimizing processes, SMART maintains focus. For example: “Reduce technical debt by 20% per quarter by refactoring priority modules.” SMART goals should be used in digital product management at all stages to achieve sustainable and predictable results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Unlike traditional project management, where goals often remain static, digital products thrive when SMART criteria evolve with each lifecycle phase — always specific, always measurable, but continuously refined based on real-world learning.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  SMART Goals vs. General Tasks
&lt;/h2&gt;

&lt;p&gt;General statements like “improve the application” or “increase productivity” lack clarity and measurability.&lt;/p&gt;

&lt;p&gt;SMART goals, on the contrary, help to eliminate vague task statements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bad:&lt;/strong&gt; “Make the website faster”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Good (according to SMART):&lt;/strong&gt; “Reduce the average load time of the main page by 30% within 2 months”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another benefit of SMART goals is that they work on the motivation and involvement of the team, since each participant in the product development process understands where the project is heading and how progress is measured.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;SMART goals act as both compass and speedometer — keeping teams focused while proving they're moving in the right direction.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  SMART Goals in Action: Practical Examples for Digital Teams
&lt;/h2&gt;

&lt;p&gt;The best way to understand effective goal-setting is through examples. Here's how different roles can apply the SMART framework to drive measurable results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Product team&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's say you're preparing to launch a new feature. Instead of an abstract goal of "add a category filter," you can set a SMART goal:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Implement category filtering in the catalog and test it on 80% of users during the current sprint to increase conversion by 15%."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here, there is specificity, a deadline, and a connection to product metrics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Development team&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Working with technical debt is often postponed if there is no clear goal. Example:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Optimize API requests by reducing the average response time from 700ms to 300ms in 3 weeks"&lt;/em&gt; is a clear and measurable task that fits into the backlog and affects the user experience. Agreed, this is much better than telling the team to simply refactor a certain module of the application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Business goals&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For growth, not only features are important, but also metrics like LTV or CAC. A good SMART goal:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Reduce the cost of customer acquisition (CAC) by 20% per quarter by testing new advertising channels."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The numbers, the reason, and the deadline are all there.&lt;/p&gt;

&lt;p&gt;Whether you're shipping features, optimizing systems, or driving growth, SMART goals turn intention into action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Crafting Effective SMART Goals: A Step-by-Step Guide
&lt;/h2&gt;

&lt;p&gt;Transforming vague ideas into actionable objectives is simpler than you might think. Follow this five-step framework to create powerful SMART goals for your digital product team.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Be Specific&lt;/strong&gt;&lt;br&gt;
Weak: "Improve user experience"&lt;br&gt;
Strong: "Implement push notifications for order confirmations"&lt;br&gt;
Why it matters: Specificity eliminates guesswork. Everyone understands exactly what needs to be built.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Ensure Measurability&lt;/strong&gt;&lt;br&gt;
Weak: "Get more user feedback"&lt;br&gt;
Strong: "Collect 200 in-app reviews after launching the new rating feature"&lt;br&gt;
Key benefit: Quantifiable targets let you track progress objectively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Verify Achievability&lt;/strong&gt;&lt;br&gt;
Unrealistic: "Redesign entire platform in one week"&lt;br&gt;
Practical: "Launch new registration flow by January 25 with the current dev team"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Confirm Relevance&lt;/strong&gt;&lt;br&gt;
Questionable: "Add social media sharing to admin dashboard"&lt;br&gt;
Valuable: "Implement one-click export for sales reports to reduce manager workload"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Set Time Boundaries&lt;/strong&gt;&lt;br&gt;
Vague: "Complete sometime this quarter"&lt;br&gt;
Effective: "Deploy to production by May 15"&lt;/p&gt;

&lt;p&gt;This structured approach turns overwhelming "to-dos" into clear, motivating objectives that drive real results.&lt;/p&gt;

&lt;h2&gt;
  
  
  SMART vs OKR vs KPI: Choosing the Right Framework
&lt;/h2&gt;

&lt;p&gt;Understanding the differences between these three popular methodologies is crucial for effective product management. Let's clarify when and how to use each approach — and how they can work together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SMART&lt;/strong&gt; is a way to clearly and understandably set a goal. It is great for team and individual tasks, especially if you work in an agile team or a startup, where focus is more important than large-scale strategies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OKR (Objectives and Key Results)&lt;/strong&gt; is about big ambitions. You set an inspiring goal (Objective) and 2-4 key results to measure your progress. SMART formulations fit perfectly into OKR key results. You can think of OKR as an add-on to SMART goals. OKR is about a strategy for the year, SMART is about sprint planning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KPI (Key Performance Indicators)&lt;/strong&gt; are metrics used to track ongoing performance and efficiency. They answer the question: “How well are we doing?” For example, a KPI could be: “Reduce the churn rate to 10%.”Some KPIs can be part of a SMART goal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In summary:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Need to solve a specific problem? → SMART&lt;/li&gt;
&lt;li&gt;Setting a company-wide strategy? → OKR&lt;/li&gt;
&lt;li&gt;Tracking ongoing performance? → KPI&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;These frameworks don’t compete — they complement each other in different areas of product management. Used together, they create a powerful system for turning vision into measurable results.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Common SMART Goal Pitfalls and Solutions
&lt;/h2&gt;

&lt;p&gt;Even experienced product teams often stumble when implementing SMART goals. Here are three critical mistakes that undermine digital product initiatives — and how to fix them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Unrealistic ambitions that backfire&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem Example:&lt;/strong&gt; "Increase conversion rates by 300% within 30 days"&lt;br&gt;
&lt;strong&gt;Why It Fails:&lt;/strong&gt; While aggressive targets can inspire, impossible goals destroy morale. Teams quickly recognize when leadership prioritizes wishful thinking over achievable outcomes.&lt;br&gt;
&lt;strong&gt;SMART Solution:&lt;/strong&gt; "Grow conversions by 18% this quarter through checkout flow optimization and targeted email campaigns"&lt;br&gt;
SMART implies realism and achievability, not a flight of fancy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Lack of metrics&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Problem Example:&lt;/strong&gt; "Improve the user interface"&lt;br&gt;
&lt;strong&gt;Why It Fails:&lt;/strong&gt; Without measurable criteria, "improvement" becomes subjective. Teams waste time debating interpretations rather than executing.&lt;br&gt;
&lt;strong&gt;SMART Solution:&lt;/strong&gt; "Increase primary CTA click-through rate from 12% to 15% within 6 weeks via UI refinements validated through A/B testing"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Loss of connection with business goals&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Problem Example:&lt;/strong&gt; "Redesign admin dashboard with new analytics widgets" (while cart abandonment hits 65%)&lt;br&gt;
&lt;strong&gt;Why It Fails:&lt;/strong&gt; Teams often optimize what's measurable rather than what matters. Internal-facing "improvements" frequently divert resources from core user needs.&lt;br&gt;
&lt;strong&gt;SMART Solution:&lt;/strong&gt; "Reduce cart abandonment by 25% in Q2 through streamlined checkout and exit-intent promotions"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Golden Rule:effective SMART goals answer three questions:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Clear&lt;/strong&gt; - Does everyone understand exactly what to do?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Valuable&lt;/strong&gt; - Will this actually move key metrics?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timely&lt;/strong&gt; - Is this the most impactful use of resources now?&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;By pressure-testing goals against these criteria, teams avoid busywork and focus on what truly drives product success.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  SMART Goals for Product Managers
&lt;/h2&gt;

&lt;p&gt;A product manager (PM) guides the product in the right direction by clearly defining priorities. SMART goals help PMs clearly formulate priorities for themselves and their teams.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Individual Goals&lt;/strong&gt;&lt;br&gt;
Example: “Conduct 10 interviews with key customers by the end of the month and prepare 2 hypotheses for testing.”  This type of goal develops Product Discovery skills and keeps the focus on real user needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Team Goals&lt;/strong&gt;&lt;br&gt;
Example: “Increase Retention from 20% to 30% per quarter by improving onboarding processes.” Here, it is important that the goal is aligned with development, design, and is based on data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quarterly or Annual Goals&lt;/strong&gt;&lt;br&gt;
SMART goals also work well over longer distances. For example:&lt;br&gt;
“Launch a new version of the product in three countries with at least 35% user retention during the first two weeks of use by the end of Q3.”&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For PMs, SMART isn’t only about task control, but also about shaping a logical, strategic approach to product development aligned with business goals.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  SMART and Agile teams
&lt;/h2&gt;

&lt;p&gt;Agile implies flexibility, but flexibility does not mean a lack of structure. SMART goals fit perfectly into the agile approach, especially if they are properly integrated into sprints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In sprint planning&lt;/strong&gt;, you can use SMART as a basis for user stories and tasks. For example: "Increase the filter response speed on mobile devices to &amp;lt;1 second in 2 sprints."&lt;/p&gt;

&lt;p&gt;During &lt;strong&gt;retrospectives&lt;/strong&gt;, reviewing SMART goals helps evaluate what worked and what did not. This gives food for reflection and helps avoid repeating mistakes.&lt;/p&gt;

&lt;p&gt;Tools like &lt;strong&gt;Jira, ClickUp&lt;/strong&gt;, and &lt;strong&gt;Notion&lt;/strong&gt; are excellent for recording and tracking SMART goals. The main thing is not to make them visible and regularly discussed during planning, demos, and retros.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Agile and SMART complement each other by making teams predictable without creating unnecessary processes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How to Implement SMART in a Development Team
&lt;/h2&gt;

&lt;p&gt;Setting goals is half the battle. The main thing is to make sure that the entire product development team embraces this approach as part of their daily workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Training and Workshops&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not everyone in the team immediately understands why SMART is important and how to apply it. It is useful to conduct short workshops where the difference between a “vague” and a “smart” goal is demonstrated using real tasks. It is good to introduce quick exercises, where each team member sets 1–2 SMART goals relevant to their roles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Templates and examples&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;People are willing to repeat if there is an easy-to-use structure. Templates in Notion or Google Docs, structured clearly with fields such as "What are we doing?", "Why?", "How do we measure success?", and "When will we complete it?", lower the entry barrier, clarify expectations, and build good habits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handling Objections&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Occasional pushback—"We already manage fine, why use SMART?"—is common. Highlight that SMART is not about bureaucracy but clarity and efficiency, saving time and reducing stress by clearly tracking progress instead of merely "doing tasks."&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools and Templates for SMART Goals
&lt;/h2&gt;

&lt;p&gt;To successfully embed SMART goals into your workflow, you'll need more than just understanding—you need the right tools. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tables (Excel, Google Sheets)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a classic — simple, clear, accessible to everyone. You can create tabs by sprints, teams, or people, and use color coding of progress. Important: keep the table alive, not abandoned.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Notion and ClickUp&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Great for structured work. You can create a task template with fields: Specific, Measurable, Achievable, and so on. Notion is also convenient for visualizing progress, for example, through dashboards.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Plugins and Project Management Tools&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For Jira, Trello, Asana, and other planners, some plugins and extensions add SMART support. Some allow you to set goals and immediately link them to tasks, which makes it easier to track in real time.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;While tool choice depends on team maturity, the core requirements remain constant: goals must be visible, accessible, and regularly updated. Without these elements, even the best intentions get lost in daily operations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How to Evaluate and Adapt SMART Goals
&lt;/h2&gt;

&lt;p&gt;Even perfectly crafted SMART goals can become outdated. That's why continuous evaluation is just as crucial as initial goal-setting, especially in dynamic product environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Review cycle: month, quarter&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Regular reviews (monthly or quarterly) are critical, particularly for agile teams where priorities frequently shift. Embrace goal revision as a pathway to success rather than stubbornly adhering to outdated objectives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Visualization of progress&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Clear visual progress tracking — dashboards, graphs, or simple traffic light indicators (red, yellow, green) — in tools like Notion or ClickUp boosts team motivation and ensures goals remain front-and-center rather than forgotten in documents.&lt;/p&gt;

&lt;p&gt;This approach turns SMART goals from static targets into living guides for your product's evolution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: Why Product Teams Need SMART
&lt;/h2&gt;

&lt;p&gt;Clear goals accelerate product growth; vague ones lead to stagnation. Without well-defined objectives, teams flounder, metrics plateau, and users lose faith in progress.&lt;/p&gt;

&lt;p&gt;The SMART framework transforms this dynamic by helping digital teams:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Focus on what is important.&lt;/li&gt;
&lt;li&gt;Clarify and measure work effectively.&lt;/li&gt;
&lt;li&gt;Track progress and celebrate achievements.&lt;/li&gt;
&lt;li&gt;Avoid burnout and chaotic decisions.&lt;/li&gt;
&lt;li&gt;Increase productivity not through clarity, not extra hours.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For teams new to SMART goals, start small:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set one SMART goal for a sprint.&lt;/li&gt;
&lt;li&gt;Test simple templates in daily standups.&lt;/li&gt;
&lt;li&gt;Review outcomes during retrospectives.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key is simply to start. The measurable improvements in alignment, progress, and results will clearly demonstrate SMART’s value.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Author: Andrey Stepanov, CTO at Byteminds&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
    </item>
    <item>
      <title>Building Teams for Digital Products: Essential Roles, Methods, and Real-World Advice</title>
      <dc:creator>Byteminds Agency</dc:creator>
      <pubDate>Wed, 11 Jun 2025 10:46:06 +0000</pubDate>
      <link>https://dev.to/byteminds_agency/building-teams-for-digital-products-essential-roles-methods-and-real-world-advice-2m0o</link>
      <guid>https://dev.to/byteminds_agency/building-teams-for-digital-products-essential-roles-methods-and-real-world-advice-2m0o</guid>
      <description>&lt;p&gt;A digital product is more than just a set of features or an interface. Creating it is a process that demands not only technical expertise but also effective team organization.&lt;/p&gt;

&lt;p&gt;Product development involves high uncertainty at every stage, requiring each team member to mitigate risks and adapt to change actively.&lt;br&gt;
In this article, we’ll explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The essential roles in digital product development&lt;/li&gt;
&lt;li&gt;The pros and cons of working with freelancers, outsourcing, and in-house teams&lt;/li&gt;
&lt;li&gt;How to choose the right collaboration model for your project&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Product Development Isn’t About Websites
&lt;/h2&gt;

&lt;p&gt;Website or landing page development follows a predictable path: predefined layouts, structured pages, and minimal uncertainty. Products, however, are dynamic and continuously evolving. At every stage, new questions arise, like, “What’s more important: refining the interface or quickly releasing a new feature?” forcing teams to pivot tasks and strategies.&lt;/p&gt;

&lt;p&gt;This is why product development teams differ significantly: they require more than just a group of specialists. They need an ecosystem designed to handle constant change.&lt;/p&gt;

&lt;p&gt;Yet in practice, many fail to grasp this difference or even see its necessity. &lt;/p&gt;

&lt;p&gt;This “design-first, develop-fast” mindset is common but fundamentally flawed for product development. &lt;/p&gt;

&lt;p&gt;Now that we’ve set the stage, let’s delve into the nuances of team collaboration models.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Roles in a Product Team
&lt;/h2&gt;

&lt;p&gt;For a digital product to progress rather than stall indefinitely, each role must own its responsibilities and actively reduce uncertainty. &lt;/p&gt;

&lt;p&gt;Below, we break down the core roles that drive progress.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Product Owner: The Strategic Navigator&lt;/strong&gt;&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%2F6z3up7gi4ncvpmagvznb.jpg" 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%2F6z3up7gi4ncvpmagvznb.jpg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Product Owner (PO) ensures business objectives aren’t lost in endless refinements. This role demands deep market understanding, the ability to prioritize team ideas, and the judgment to decide what’s essential now and what can wait.&lt;/p&gt;

&lt;p&gt;For instance, if developers propose a complex feature, the PO assesses: Will this actually deliver business value? &lt;/p&gt;

&lt;p&gt;Their expertise should span:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The product’s core purpose and its competitive landscape&lt;/li&gt;
&lt;li&gt;Market-specific constraints and opportunities&lt;/li&gt;
&lt;li&gt;Audience pain points and unmet needs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These insights help the Product Owner develop meaningful hypotheses and make informed decisions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Important: Often, the PO is the client or a stakeholder with market expertise. Their role is to minimize strategic uncertainty by setting priorities and focusing on valuable solutions. Decisiveness here is crucial—without it, the risk of developing irrelevant solutions is high.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Product Producer: Bridging Strategy and Execution&lt;/strong&gt;&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%2F801uix1zs12k5x36ggkd.jpg" 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%2F801uix1zs12k5x36ggkd.jpg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Far more than just a project manager, the Product Producer acts as the critical link between business goals and technical execution. They reduce chaos, manage client expectations, and shield the team from unnecessary scope changes. &lt;/p&gt;

&lt;p&gt;If a business demands a complex feature that developers estimate will take months, the Producer finds a compromise, like delivering an MVP sooner or suggesting an alternative solution.&lt;/p&gt;

&lt;p&gt;Beyond task decomposition and timeline management, the Producer understands business priorities deeply. This additional insight helps him make better decisions during the product development process.&lt;/p&gt;

&lt;p&gt;The Product Producer is an enhanced version of a project manager and the right-hand to the Product Owner. They organize tasks, establish transparent processes, and build predictable workflows. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Critical Note: If the Product Owner hasn't adequately prioritized tasks, the Producer can partially mitigate this, but can’t fully replace the strategic vision of the Product Owner.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Business Analyst: Architect of Cohesive Systems&lt;/strong&gt;&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%2Fzvkq0baq8xr7y53o1cck.jpg" 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%2Fzvkq0baq8xr7y53o1cck.jpg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Business Analyst’s main role is to serve as the vital bridge between conceptual requirements and practical implementation. Their core mission is to ensure that all product requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are technically feasible and internally consistent&lt;/li&gt;
&lt;li&gt;Contribute to a unified system architecture&lt;/li&gt;
&lt;li&gt;Account for critical edge cases and real-world usage scenarios&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By thoroughly understanding how different system components interact (or should interact), the Analyst transforms abstract ideas into clear, actionable tasks for the development team. Their technical discernment helps identify whether a proposed feature provides genuine value or simply becomes unnecessary complexity — what we might call "attaching a fifth wheel to a car."&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Key Impact: The Analyst helps the team navigate ambiguous requirements, highlight key aspects, and focus on the main thing, reducing tactical uncertainty.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;UX Designer: Crafting Intuitive Experiences&lt;/strong&gt;&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%2Fmta8zchhhzkgtts9u0ds.jpg" 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%2Fmta8zchhhzkgtts9u0ds.jpg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Far more than just creating visually appealing interfaces, the UX Designer engineers user journeys that are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Intuitively navigable&lt;/li&gt;
&lt;li&gt;Purposefully structured&lt;/li&gt;
&lt;li&gt;Optimized for the target audience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For mass-market products, this means prioritizing simplicity and accessibility above all. The designer's prototypes and wireframes serve as the blueprint for how real users will interact with and derive value from the product.&lt;/p&gt;

&lt;p&gt;Superior UX design minimizes two critical risks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User frustration caused by confusing interfaces&lt;/li&gt;
&lt;li&gt;Lost conversions due to poor experience design&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By establishing predictable, user-friendly interaction patterns, the UX Designer ensures customers can effortlessly achieve their goals within the system, without getting lost or discouraged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Team Lead: Guardian of Technical Excellence&lt;/strong&gt;&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%2F08st7yub93w73a32rkoo.jpg" 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%2F08st7yub93w73a32rkoo.jpg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A skilled development team alone isn't enough — without technical leadership, projects can quickly derail.&lt;/p&gt;

&lt;p&gt;The Team Lead serves as the architectural cornerstone, ensuring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Robust system design that withstands growth&lt;/li&gt;
&lt;li&gt;Consistent engineering practices across the team&lt;/li&gt;
&lt;li&gt;Production-ready code quality&lt;/li&gt;
&lt;li&gt;Scalability planning from day one&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without this role, teams risk creating disposable solutions that crumble under real-world loads. The Team Lead reduces technical uncertainty by making informed architectural choices, ensuring code quality, selecting appropriate tools, assigning tasks based on developer skills, and implementing rigorous testing.&lt;/p&gt;

&lt;p&gt;Have you heard of the Three Amigos principle? It’s a collaborative communication method where requirements, potential scenarios, and risks are discussed jointly beforehand.  This significantly reduces misunderstandings and increases product quality.&lt;/p&gt;

&lt;p&gt;So, one of the three amigos will often be the Team Leader, who dives into the product before the start of development to properly reduce technical uncertainty during further work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Development Team: Execution Powerhouse&lt;/strong&gt;&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%2Fl36ynjfd5qdzt6x52v3k.jpg" 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%2Fl36ynjfd5qdzt6x52v3k.jpg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Comprising developers and QA specialists, this group executes tasks, writes code, tests, and implements features.&lt;/p&gt;

&lt;p&gt;Crucially, they must collaborate effectively with other roles rather than merely follow instructions. Their clear task execution, adaptability to change, and attention to detail significantly reduce project uncertainty.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Your Product Team: Choosing the Right Approach
&lt;/h2&gt;

&lt;p&gt;Creating a successful digital product requires more than just talent—it demands the right team structure. You essentially have three options: hiring freelancers, partnering with an outsourcing firm, or building an in-house team. Each approach has its trade-offs, and the best choice depends on your project’s needs, budget, and long-term goals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Freelancers: Cheap, Flexible, and Fragmented&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At first glance, freelancers seem appealing: they’re cost-effective, available on demand, and can handle specific tasks. But assembling a full product team from freelancers is often a recipe for frustration. While they excel at executing well-defined assignments, they rarely engage deeply with the product vision or collaborate effectively with others.&lt;/p&gt;

&lt;p&gt;The core issue isn’t skill—it’s alignment. Freelancers work transactionally, not strategically. Without strong product management on your side, coordination can quickly become chaotic. Deadlines slip, communication falters, and what should be a cohesive product often ends up as a patchwork of disjointed solutions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When it works:&lt;/strong&gt; For one-off, short-term tasks, but not for building an entire product.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Outsourcing: Speed, Expertise, and Lower Risk&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Outsourcing strikes a balance between flexibility and structure. A competent agency handles everything from scoping to execution — ideal if you lack the time, resources, or expertise to manage development internally. Even if you already have an in-house team, outsourcing can accelerate your MVP launch by offering fresh perspectives and avoiding internal bottlenecks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faster execution&lt;/strong&gt; – experienced teams avoid common pitfalls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Established processes&lt;/strong&gt; – they deliver structured, scalable solutions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lower risk&lt;/strong&gt; – if the project fails, costs are contained; if it succeeds, you can transition it in-house&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The catch?&lt;/strong&gt; Choosing the right partner. Vet their portfolio, clarify expectations upfront, and ensure contractual alignment on goals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In-House Teams: Commitment at a Cost&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A dedicated internal team is the gold standard for long-term product development. Your employees live and breathe the product, adapt quickly to change, and drive sustained innovation. The catch? It’s expensive and demanding.&lt;/p&gt;

&lt;p&gt;Building an in-house team means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High recruitment stakes&lt;/strong&gt; – finding skilled people who align with your vision takes time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ongoing investment&lt;/strong&gt; – salaries, training, and culture-building aren’t one-time efforts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process overhead&lt;/strong&gt; – you’ll need strong leadership to maintain cohesion and productivity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mistakes are costly&lt;/strong&gt; – mis-hires or poor management can derail progress entirely. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But when done right, an in-house team becomes your greatest competitive edge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Bottom Line:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Freelancers&lt;/strong&gt; - only for discrete tasks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outsourcing&lt;/strong&gt; - ideal for MVPs and resource-strapped teams&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In-house&lt;/strong&gt; - the best choice—if you can sustain it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your team structure isn’t just about talent—it’s about how well that talent works together. Choose wisely.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Art of Effective Teamwork: Principles That Drive Success
&lt;/h2&gt;

&lt;p&gt;Building a high-performing product team isn’t about rigid processes or perfect plans—it’s about fostering the right kind of collaboration. Here’s what truly makes teams work.&lt;/p&gt;

&lt;p&gt;Communication is one of the few points that consistently drives results. But effective communication isn’t about weekly reports or ritual demos—it’s about active dialogue. Teams need space to debate ideas, challenge assumptions, and co-create solutions. Still, there’s a fine line between productive discussion and wasted time.&lt;/p&gt;

&lt;p&gt;We’ve found that structured collaboration prevents misalignment. The magic happens when both sides engage authentically — not as "client and contractor," but as partners willing to listen, adapt, and occasionally compromise.&lt;/p&gt;

&lt;p&gt;It’s also critically important to understand who is responsible for what. Simply assigning roles is not enough — each team member needs to perform effectively within their area and avoid overstepping into others’ domains.&lt;/p&gt;

&lt;p&gt;Clear roles matter, but boundaries matter more. Consider this cautionary tale:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;strong&gt;Product Producer&lt;/strong&gt; skips stakeholder conversations and relies solely on metrics → surfaces flawed insights&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Analyst&lt;/strong&gt; wastes time untangling those assumptions instead of focusing on discovery&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;UX Designer&lt;/strong&gt;, lacking direction, makes arbitrary interface changes&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Team Lead&lt;/strong&gt; gets dragged into priority debates instead of safeguarding the architecture&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developers&lt;/strong&gt; juggle conflicting tasks, creating technical debt&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The result?&lt;/strong&gt; Escalating uncertainty and operational chaos. True ownership means excelling in your role while trusting others to do the same.&lt;/p&gt;

&lt;p&gt;The next important thing is the project plan. A good project plan is a compass, not shackles. Products change, and that’s normal. The ability to adapt quickly is what makes teams resilient. Flexibility, however, doesn’t mean disorder: a shared direction helps teams pivot while staying aligned.&lt;/p&gt;

&lt;p&gt;And finally — people. They are the most valuable asset in any project. Invest in them. Your team’s growth directly impacts your product’s quality. When you provide learning opportunities, encourage professional development, and create psychological safety, you don’t just get employees — you get invested partners who bring their best thinking to tough challenges.&lt;/p&gt;

&lt;p&gt;The difference between good and exceptional work rarely comes down to process. It comes down to how your team engages — with the product and with each other.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Author: Andrey Stepanov, CTO at ByteMinds&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Goals in Digital Development: How to Launch a Digital Product Without Failure</title>
      <dc:creator>Byteminds Agency</dc:creator>
      <pubDate>Thu, 29 May 2025 10:23:41 +0000</pubDate>
      <link>https://dev.to/byteminds/goals-in-digital-development-how-to-launch-a-digital-product-without-failure-55j9</link>
      <guid>https://dev.to/byteminds/goals-in-digital-development-how-to-launch-a-digital-product-without-failure-55j9</guid>
      <description>&lt;h2&gt;
  
  
  The Client Knows What They Want, but That’s Not Enough
&lt;/h2&gt;

&lt;p&gt;When clients approach a development team, they’ve often already put significant thought into their vision. They have a clear mental image of the final product, and sometimes even clear goals and success metrics. These ideas typically translate into functional requirements, such as:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;"The product should include a supplier account with X and Y capabilities."&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;"We need an app where users can apply for banking products."&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At first glance, this "shopping list" of features might suggest the client has everything figured out, leading the development team to take the requirements at face value. So the team might follow these requirements literally. After all, isn’t the client the expert in their own business?&lt;/p&gt;

&lt;p&gt;However, if the development team doesn’t engage in deeper discussions about these requests, their role risks being reduced to mere task execution: "Build what you’re told, exactly as specified." In such cases, the team becomes a group of passive performers rather than strategic partners. As a result, a product is created that "works according to the technical specifications," but does not necessarily help the business.&lt;/p&gt;

&lt;p&gt;A truly high-performing development team doesn’t just complete tasks — it works to achieve product goals. To foster this mindset, we immerse the team in the product’s strategic vision from day one. By understanding the bigger picture and the hypotheses behind decisions, they don’t just build features — they generate ideas for improving the product. This is what separates a team of implementers from a team of creators.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Goals Matter More Than Feature Lists
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Focus on the result, not on the tasks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consider a banking service project. You could instruct the team: “Build a loan calculator.” Or, you could frame the goal: “Double the completion rate of application forms.”&lt;/p&gt;

&lt;p&gt;The first approach limits the team to checking boxes, delivering a functional but uninspired solution. The second empowers them to ask critical questions: Should the calculator be embedded earlier in the user journey? Could automated pre-filling boost conversions? Suddenly, the team isn’t just coding; they’re problem-solving. The difference in engagement and impact is monumental.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Priorities are determined by goals&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In every development cycle, there comes a point when it becomes clear: not everything will be completed on time. Without clear goals, tasks risk becoming arbitrary or worse, irrelevant. Chaos begins: what to postpone, and what to do right now? Someone decides this based on intuition, but this is poor validation. Goals and metrics are necessary to identify what truly matters. Without objective criteria, decisions default to gut feelings or competing opinions. Stakeholders push for their "must-have" features, but without alignment, "must-have" means different things to everyone.&lt;/p&gt;

&lt;p&gt;Formalized goals change the game. They act as a filter, separating what truly drives success from what’s merely noise. It is easier to decide which tasks are critical for achieving success and which can be postponed or removed altogether.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shared Goals = Shared Responsibility&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When teams work without clear goals, responsibility falls solely on the client. The client dictates requirements; developers merely execute. But when goals are explicit and understood, the dynamic shifts. The team transitions from passive implementers to active partners. They propose solutions and challenge assumptions — because they’re invested in outcomes, not just deliverables. Responsibility for success is shared, and this is the key to a strong and motivated team and, subsequently, a successful product.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Feature Requests to Strategic Goals
&lt;/h2&gt;

&lt;p&gt;The foundation of effective goal-setting lies in the SMART framework – a goal should be Specific, Measurable, Achievable, Relevant, and Time-bound. SMART goals aren't always attainable. So, let's examine how vague requests can be reframed into actionable, results-driven goals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 1.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Banking&lt;/strong&gt;: From Basic Functionality to Active Engagement&lt;br&gt;
&lt;strong&gt;Product&lt;/strong&gt;: Mobile banking application&lt;br&gt;
&lt;strong&gt;Client Request&lt;/strong&gt;: "We need an app for users to manage accounts and loans."&lt;br&gt;
&lt;strong&gt;Result&lt;/strong&gt;: A typical “shopping list”. The dev team builds what was asked, but months later, analytics show: the app isn’t driving growth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Goal-Driven Alternative&lt;/strong&gt;: &lt;em&gt;Increase the share of active users by 20% within six months.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With this clarity, the team prioritizes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Intuitive UI/UX design&lt;/li&gt;
&lt;li&gt;Automated payment reminders&lt;/li&gt;
&lt;li&gt;Reward systems (e.g., cashback for app usage)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example 2.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Education&lt;/strong&gt;: Completion Rates Over Feature Checklists&lt;br&gt;
&lt;strong&gt;Product&lt;/strong&gt;: Online learning platform&lt;br&gt;
&lt;strong&gt;Client Request&lt;/strong&gt;: "Build a platform with video lessons, tests, and certificates."&lt;br&gt;
&lt;strong&gt;Result&lt;/strong&gt;: Platform built as requested, but most learners drop out halfway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Goal-Driven Solution:&lt;/strong&gt; &lt;em&gt;Achieve a 70% course completion rate.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This shifts focus to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gamification elements (badges, progress tracking)&lt;/li&gt;
&lt;li&gt;Personalized email nudges&lt;/li&gt;
&lt;li&gt;Interactive support features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example 3.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E-Commerce&lt;/strong&gt;: Converting Browsers to Buyers&lt;br&gt;
&lt;strong&gt;Product&lt;/strong&gt;: Online store&lt;br&gt;
&lt;strong&gt;Client Request&lt;/strong&gt;: "Implement filters, shopping cart, and checkout."&lt;br&gt;
&lt;strong&gt;Result&lt;/strong&gt;: Requirements are met, but traffic doesn’t translate into sales.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Goal-Driven Approach&lt;/strong&gt;: &lt;em&gt;Boost conversion rates by 15%.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The team focuses on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User journey optimization&lt;/li&gt;
&lt;li&gt;Smart product recommendations&lt;/li&gt;
&lt;li&gt;Streamlined checkout process&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Set Effective Goals: Tools and Frameworks
&lt;/h2&gt;

&lt;p&gt;Understanding why goals matter is just the beginning. Now let's explore how to craft them effectively using these proven tools:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Problem Understanding (PU) Framework&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a basic but crucial tool. Helps clarify the roots of client requests and align on expectations before jumping to solutions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clarifying the origin of their vision&lt;/li&gt;
&lt;li&gt;Identifying core needs behind feature requests&lt;/li&gt;
&lt;li&gt;Establishing common ground before proposing solutions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Impact Mapping&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This tool links business goals and product functions. It helps to highlight which tasks are really important for achieving goals and which can be postponed. The influence map works great when the client already has a general understanding of what needs to be done.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pro tip: Use it to identify priorities, spot redundancies, and avoid “solution jumping” (building before understanding).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;3. Hypothesis Mapping&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For ambiguous or innovative projects. Helps teams explore what users might need, test assumptions, and tie features to real value.&lt;/p&gt;

&lt;p&gt;When direction is unclear, this approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structures assumptions about user needs&lt;/li&gt;
&lt;li&gt;Validates ideas through measurable experiments&lt;/li&gt;
&lt;li&gt;Shifts focus from features to real user value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unlike impact mapping (business-focused), hypothesis mapping centers on user experience validation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Goal-Setting Playbook
&lt;/h2&gt;

&lt;p&gt;Follow this step-by-step approach:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Anchor to Business Objectives&lt;/strong&gt;&lt;br&gt;
Identify the main business goal: revenue growth, customer retention, market expansion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quantify Success&lt;/strong&gt;&lt;br&gt;
Break broad objectives into KPIs. For example:&lt;/p&gt;

&lt;p&gt;"Increase premium conversions by 30%"&lt;br&gt;
"Reduce churn to under 5% monthly"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build Your Impact Map&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Link metrics to tasks via an impact map.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generate Solutions&lt;/strong&gt; &lt;br&gt;
Use a hypothesis map to find creative solutions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test innovative approaches&lt;/li&gt;
&lt;li&gt;Validate with user feedback&lt;/li&gt;
&lt;li&gt;Iterate based on data&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember: Goals aren't static. Revisit and refine them as you gather insights from real user behavior and market response.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion: Laying the Right Foundation for Success
&lt;/h2&gt;

&lt;p&gt;Understand the product’s purpose, define success metrics, and apply the right tools to stay focused and effective. Record goals, check their relevance, and use tools like the impact map and hypothesis map.&lt;/p&gt;

&lt;p&gt;This disciplined approach doesn't just improve success rates—it transforms development from a mechanical process into a purposeful journey.&lt;/p&gt;

&lt;p&gt;Remember: With well-set goals, half the work is already done — the rest becomes clearer, more focused, and more meaningful.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Author: Andrey Stepanov, CTO at ByteMinds&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>digitaldevelopment</category>
    </item>
    <item>
      <title>How to Become a Kentico MVP - Interview with Kentico MVP Dmitry Bastron</title>
      <dc:creator>Byteminds Agency</dc:creator>
      <pubDate>Thu, 27 Mar 2025 11:52:27 +0000</pubDate>
      <link>https://dev.to/byteminds/how-to-become-a-kentico-mvp-interview-with-kentico-mvp-dmitry-bastron-4776</link>
      <guid>https://dev.to/byteminds/how-to-become-a-kentico-mvp-interview-with-kentico-mvp-dmitry-bastron-4776</guid>
      <description>&lt;p&gt;Dmitry Bastron is the Head of Development at ByteMinds and has been working with Kentico since 2016. In 2020, he earned the title of Kentico MVP — a recognition awarded to the most active and valuable members of the Kentico community.&lt;/p&gt;

&lt;p&gt;In this interview, Dmitry shares how his journey with Kentico began, what motivated him to become an MVP, and what it takes to earn the badge yourself.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How Did Your Journey with Kentico and the MVP Program Begin?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To start with, Kentico was the first CMS I ever worked with. When I joined ByteMinds, I was working with various platforms, building up my experience and skills to eventually become a senior developer. Throughout this journey, I received tremendous support and knowledge from the Kentico community. &lt;/p&gt;

&lt;p&gt;Early on, when I was still new to Kentico, I often visited &lt;a href="https://devnet.kentico.com" rel="noopener noreferrer"&gt;https://devnet.kentico.com&lt;/a&gt;, specifically the &lt;a href="https://devnet.kentico.com/questions-answers" rel="noopener noreferrer"&gt;Questions and Answers section&lt;/a&gt;. That’s where my journey with Kentico really began. Whenever I didn’t understand something, I would post my questions and always received helpful answers. &lt;/p&gt;

&lt;p&gt;Over time, I noticed the most active members of the community — people who regularly helped others —  and I came to respect them for their contributions. Some of them were Kentico MVPs, although at the time, I didn’t fully understand what that meant.&lt;/p&gt;

&lt;p&gt;As I gained more experience, I realized I had a lot of knowledge to share. That's when I decided I wanted to become a full-fledged part of the Kentico community because it had played such an important role in my growth as a developer. I started answering questions and became an active contributor.&lt;/p&gt;

&lt;p&gt;Eventually, my activity was noticed by Kentico, and, in 2019, they invited me to join the MVP program. And I thought, why not? Back then, there weren’t many specific requirements — just a general expectation to stay active throughout the year by writing articles, answering community questions, and helping promote Kentico more broadly. In 2020, I officially became a Kentico MVP and have renewed the title every year since.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why Become a Kentico MVP? What Motivated You?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When I began my path toward MVP, one of the main benefits —as highlighted by Kentico themselves—was the opportunity to connect with other top professionals and exchange knowledge. And that truly is a great perk. But for me, it wasn’t the main reason.&lt;/p&gt;

&lt;p&gt;What really attracted me was the chance to engage directly with the product team, offer feedback, and influence the development of the Kentico itself. That was the deciding factor —and to some extent, it worked out! I’ve been able to share my ideas with the developers, and some of that feedback has positively impacted the product. &lt;/p&gt;

&lt;p&gt;Another strong motivator was the annual MVP Summit. It’s an event where all MVPs gather at Kentico’s office to meet the architects and product team face to face. It’s a great chance to participate in sessions, network in person, and have some fun together. As an extrovert, this kind of environment was especially appealing to me and played a big role in my decision to join the MVP circle.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Is It Hard to Become a Kentico Developer? What Background Do You Need?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’d say the entry barrier isn’t very high. One major advantage of Kentico is its excellent documentation and developer guides, which make onboarding fast and smooth. &lt;/p&gt;

&lt;p&gt;Of course, there’s still a basic level of knowledge required. At the very least, you should have a general understanding of web development.  Experience with other CMS platforms is helpful, but not mandatory. As a first CMS, Kentico leaves a great impression, and the learning curve is quite friendly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What Do Kentico MVPs Do and Why Are They Important?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Firstly, any mid-sized or large-scale product needs an active community to help reduce the load on customer support. When users enjoy working with a product, they will naturally start sharing their experiences, helping others, and contributing to the product’s success. Kentico is no exception — it has a strong and engaged community of users and developers.&lt;/p&gt;

&lt;p&gt;Within a community, there are always a few people who stand out through their consistent involvement.  That’s how the Kentico MVP group is formed: it's made up of those particularly active contributors who go above and beyond to support others and help the community grow.&lt;/p&gt;

&lt;p&gt;The other important and equally valuable aspect of the MVP program, which Kentico highly appreciates, is the opportunity to get feedback on how the product is being used —especially from Kentico itself. &lt;/p&gt;

&lt;p&gt;Since CMS developers don’t typically build websites using their own CMS, this creates a challenge: it’s difficult for them to assess how well and effectively their product works in real-world scenarios. They need an external perspective — ideally from two angles: the user’s experience (how intuitive the interface is, how easy it is to perform daily tasks) and whether the  CMS meets technical needs (performance, flexibility, feature sets, etc.). &lt;/p&gt;

&lt;p&gt;For a long time, the Kentico MVP program focused only on technical experts. But recently Marketing MVPs have been introduced — people without a technical background but with expertise in marketing. Their perspective is extremely valuable. While technical experts focus on improving the platform from a technical standpoint, they sometimes overlook a key element: the real-world importance and impact of those features. This is where Marketing MVPs shine. They can articulate why a specific feature matters, what it enables users to achieve, and how it contributes to the success of a site or product. They help explain the "why" behind the "what."&lt;/p&gt;

&lt;p&gt;This year brought changes to the community program. In addition to MVPs, Kentico has introduced a new title: &lt;a href="https://community.kentico.com/blog/kentico-community-programs-in-2025-mvps-and-community-leaders" rel="noopener noreferrer"&gt;Community Leader&lt;/a&gt;.  While this role carries fewer benefits, it serves to recognize individuals who are more focused on growing and supporting the community itself rather than directly contributing to product development. Kentico wants its community to grow, thrive, and become more visible — and this is a great first step for anyone aiming to eventually become an MVP.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How Much Time and Effort Does It Take to Become a Kentio MVP? What Actions Are Required?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Applications are typically accepted at the beginning of December, so if you're aiming to become an MVP, you’ll need to stay active throughout the year. Consistent engagement is a key requirement.&lt;/p&gt;

&lt;p&gt;But no one will track your activity for you. If you're interested in becoming a Kentico MVP, your first step should be to contact &lt;a href="https://community.kentico.com/member/19" rel="noopener noreferrer"&gt;Sean Wright&lt;/a&gt;,  who currently manages the MVP program. He can help you find the right mix of activities that will boost your chances of selection.&lt;/p&gt;

&lt;p&gt;So, what types of activities does Kentico expect? Primarily, you’ll need to focus on promoting Kentico and actively engaging with the community. This might include: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Writing blog posts&lt;/strong&gt; on various topics about Kentico features or your experience using the platform. These don’t have to be glowing reviews, just honest and practical experiences.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Speaking at conferences&lt;/strong&gt;, whether they are tech-focused or broader in scope, where you can include Kentico in your presentations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Developing and publishing modules or extensions&lt;/strong&gt; that improve day-to-day experience for developers, content editors, or marketers using Kentico.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Helping others in the community is also valuable. However, if this is your sole focus, the Community Leader title may be a better fit for you.&lt;/p&gt;

&lt;p&gt;The best way to start is by trying out different types of contributions and finding what you enjoy. Most importantly, your efforts should come from genuine interest and passion. The MVP program welcomes a wide range of contributions—there’s space for everyone.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Are There Benefits of Being a Kentico MVP?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One of the best perks of MVP status is participation in the annual Kentico Connection conference. If you present a session, you get a free ticket. MVPs also receive early access to upcoming features, get insider knowledge about Kentico's roadmap, and join regular calls with the MVP team.&lt;/p&gt;

&lt;p&gt;This early insight can be incredibly helpful for your work, as it allows you to plan ahead and adapt your projects accordingly. You become more valuable to your agency because you're staying ahead of the curve.&lt;/p&gt;

&lt;p&gt;You can find out a full list of MVP benefits in Sean's post here: &lt;a href="https://community.kentico.com/blog/you-could-be-a-kentico-mvp" rel="noopener noreferrer"&gt;https://community.kentico.com/blog/you-could-be-a-kentico-mvp&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How Popular Is the Kentico Platform, and What Are the Career Prospects for Kentico Professionals?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Kentico’s popularity is moderate but growing. More and more businesses are transitioning to Xperience by Kentico (XbyK) and the product team is making impressive progress— releasing new features monthly and continuously enhancing the platform. It looks like a promising product with a bright future.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Any Personal Advice for Those Interested in Working with Kentico and Becoming an MVP?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The MVP community is a fantastic group of people. I genuinely enjoy interacting with them — many have become personal friends. They’re smart, driven, and passionate professionals, making it a great space to exchange ideas and grow.&lt;/p&gt;

&lt;p&gt;In some larger platforms, the MVP programs include dozens or even hundreds of members, which can make it harder to form real connections. At Kentico, there are only 15 MVPs this year. That’s big enough for diversity, but small enough to feel like a close-knit family.&lt;/p&gt;

&lt;p&gt;And with that, I want to once again congratulate all of this year's Kentico MVPs. They’re an incredible group of professionals — dedicated, passionate, and genuinely supportive of both the product and the community.&lt;/p&gt;

&lt;p&gt;They absolutely deserve the recognition, and it's a pleasure to work alongside them:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.kentico.com/member/28" rel="noopener noreferrer"&gt;Liam Goldfinch&lt;/a&gt;&lt;br&gt;
&lt;a href="https://community.kentico.com/member/109" rel="noopener noreferrer"&gt;Ben Quinlan&lt;/a&gt; &lt;br&gt;
&lt;a href="https://community.kentico.com/member/37" rel="noopener noreferrer"&gt;Elmar Höfinghoff&lt;/a&gt;&lt;br&gt;
&lt;a href="https://community.kentico.com/member/85" rel="noopener noreferrer"&gt;Michael Eustace&lt;/a&gt;&lt;br&gt;
&lt;a href="https://community.kentico.com/member/76" rel="noopener noreferrer"&gt;Trevor Fayas&lt;/a&gt;&lt;br&gt;
&lt;a href="https://community.kentico.com/member/133" rel="noopener noreferrer"&gt;Andy Thompson&lt;/a&gt;&lt;br&gt;
&lt;a href="https://community.kentico.com/member/154" rel="noopener noreferrer"&gt;Derek Barka&lt;/a&gt;&lt;br&gt;
&lt;a href="https://community.kentico.com/member/61" rel="noopener noreferrer"&gt;Mike Wills&lt;/a&gt; &lt;br&gt;
&lt;a href="https://community.kentico.com/member/124" rel="noopener noreferrer"&gt;Wesley McChristian&lt;/a&gt;&lt;br&gt;
&lt;a href="https://community.kentico.com/member/259" rel="noopener noreferrer"&gt;Joe Peschardt&lt;/a&gt;&lt;br&gt;
&lt;a href="https://community.kentico.com/member/21" rel="noopener noreferrer"&gt;Brian McKeiver&lt;/a&gt; &lt;br&gt;
&lt;a href="https://community.kentico.com/member/73" rel="noopener noreferrer"&gt;Jeroen Fürst&lt;/a&gt;&lt;br&gt;
&lt;a href="https://community.kentico.com/member/43" rel="noopener noreferrer"&gt;Dmitry Bastron&lt;/a&gt;&lt;br&gt;
&lt;a href="https://community.kentico.com/member/33" rel="noopener noreferrer"&gt;Roel Kuik&lt;/a&gt;&lt;br&gt;
&lt;a href="https://community.kentico.com/member/195" rel="noopener noreferrer"&gt;Łukasz Skowroński&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Author: Dmitry Bastron, Head of Development at ByteMinds / Kentico MVP&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>kentico</category>
      <category>xbyk</category>
    </item>
    <item>
      <title>How can a team lead figure out a new project when nothing is clear?</title>
      <dc:creator>Byteminds Agency</dc:creator>
      <pubDate>Tue, 04 Mar 2025 10:42:28 +0000</pubDate>
      <link>https://dev.to/byteminds/how-can-a-team-lead-figure-out-a-new-project-when-nothing-is-clear-4j8k</link>
      <guid>https://dev.to/byteminds/how-can-a-team-lead-figure-out-a-new-project-when-nothing-is-clear-4j8k</guid>
      <description>&lt;p&gt;Hello! My name is Maria, and I am a team lead and backend developer at ByteMinds. Life as a team lead would be easier if every new project started from scratch. But the reality is different: you often have to take on someone else's code, understand the old architecture, catch the elusive context, and organize chaos.&lt;/p&gt;

&lt;p&gt;It becomes even more difficult when a project is handed over from another team – especially when documentation is fragmented, decisions are unclear, and dependencies are not obvious. So, how can you quickly get up to speed, avoid getting lost in uncertainty, and organize the process in a way that helps the team work productively?&lt;/p&gt;

&lt;p&gt;In this article, I will share my experience: I’ll explain how to understand a complex project, establish processes, and make it feel like "your own".&lt;/p&gt;

&lt;h2&gt;
  
  
  How not to drown in a new project
&lt;/h2&gt;

&lt;p&gt;Every new project comes with some (or a lot of) stress. But it becomes manageable if you know which strings to pull. Here are a few steps that can help:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set the vector&lt;/strong&gt;: why are we doing this? Understand why your project exists in the first place. Sales? Improving UX? Adding new functionality? A team lead cannot be effective if they don’t understand the end goal. Start by talking to the business or client to identify key success indicators, and then try to mentally” align these goals with your work. Clarify the main user scenarios.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Draw a map&lt;/strong&gt;: architecture under a magnifying glass. You should have a clear diagram of how all the system components interact. Which services interact with which systems? What is data stored? Study the documentation if it exists; if it doesn’t, start gathering it yourself. Make sure that every team member has access to up-to-date information. Validate the existing data: work with the current team and the client to ensure that the data is still relevant and usable.&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%2F79oyc8y3smvenukj26st.jpg" 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%2F79oyc8y3smvenukj26st.jpg" alt="Image description" width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the onboarding stage, you don't need to go into implementation details. You just need to understand how current the documentation is, how to set up the project, and what integrations, logging, and monitoring are in place. Pay special attention to sections where you’re unsure how to approach them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remove chaos&lt;/strong&gt;: the access matrix. When a project involves many people and systems, access chaos is the first thing that needs to be sorted out. Create an access matrix: specify who should have access to which systems and who should not. This table will be a lifesaver when onboarding new employees and will save time. Simply walk new employees through the access matrix and quickly reach out to the client if any permissions are missing.&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%2Fdmqnkpacfvdx673ricn4.jpg" 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%2Fdmqnkpacfvdx673ricn4.jpg" alt="Image description" width="800" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Handovers: secrets to successful knowledge transfer
&lt;/h2&gt;

&lt;p&gt;Handovers are sessions where the "old hands" of the project pass on essential knowledge to newcomers. Many people treat them as formalities, but that’s a huge mistake.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Focus&lt;/strong&gt;: topic-specific sessions. Break handovers into distinct topics: architecture, deployment, QA, etc. This allows for better focus and preparation. Before each meeting, review any available documentation on the topic and prepare questions. Be sure to share these questions with the participants in advance to avoid wasting time on unnecessary discussions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ask uncomfortable questions&lt;/strong&gt;. Inquire about typical mistakes the old team made. What are the most difficult aspects of the project?  Are there important, non-obvious features that aren’t documented anywhere? What information is missing if you need to upload changes or run tests tomorrow? You can collect these questions in advance and send them to the client before the call – this will make the conversation more meaningful and allow everyone to focus on the essentials.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't lose information&lt;/strong&gt;: record meetings. Record all handovers and keep the recordings publicly accessible. This will be useful if a new team member wants to quickly get up to speed. Even months later, you can refer back to these recordings to recall details. New team members can simply review the recordings instead of bombarding more experienced colleagues with endless questions in the chat. &lt;/p&gt;

&lt;h2&gt;
  
  
  Plan wisely: tasks and priorities
&lt;/h2&gt;

&lt;p&gt;How can you distribute tasks to make sure everything important gets done, without wasting resources? The keys are planning and auditing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understand the backlog&lt;/strong&gt;. Review the current backlog. Which tasks are critical? Which ones can be postponed? For example, if you know the project will be redesigned in a year, then working on architectural updates may be unnecessary. Prioritize tasks based on the overall goals of the project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conduct an audit&lt;/strong&gt;: this is your insurance. Even if the client doesn’t require an audit, do it for yourself. Evaluate the architecture, integrations, and technical debt. This will help identify weak points of the system and serve as your safety net if something goes wrong.&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%2Fa027soyfby6eezkhjzkh.jpg" 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%2Fa027soyfby6eezkhjzkh.jpg" alt="Image description" width="800" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An audit helps you define the risks and limitations early on. If a client suddenly needs an implementation that the system cannot support, you’ll always have something to show them.  The biggest challenge here is usually the “blank slate” problem  – you don’t know how much time it will take or what should be done first. You can dig through the code endlessly, but there’s no point in doing so. Focus on key priorities. For example, look only at the issues to the current backlog and the feature closest to implementation. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sometimes you need to reassess an estimate&lt;/strong&gt; – and this shouldn’t seem strange to you, the team, or the client. There are situations of uncertainty where it feels like the estimate might be way off. In such cases, make an estimate for research, not implementation. Clients will have realistic expectations – they’ll know what you are currently working on. Developers won’t be tied to false estimates, meaning they won’t have to work late or disappoint the client with incomplete tasks.&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%2F09mvj0ctdaouapy9xtr1.jpg" 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%2F09mvj0ctdaouapy9xtr1.jpg" alt="Image description" width="800" height="646"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Experiment with Proof of Concept (PoC)&lt;/strong&gt;. If you’re unsure whether the chosen approach will work, create a PoC. This is a small-scale experiment to test if your idea will work. The key here is not to get bogged down in the details. Remember, the goal of PoC is to determine whether the idea can be implemented, not to produce a finished product.&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%2Fttliuu228l6ryudv9763.jpg" 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%2Fttliuu228l6ryudv9763.jpg" alt="Image description" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment without stress: setting up releases
&lt;/h2&gt;

&lt;p&gt;Managing releases is another key aspect of a team lead’s role. Not everything that sounds simple turns out to be so in practice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automate where possible&lt;/strong&gt;. Find out how the project pipelines are set up. Are there fully automated processes? If not, figure out which stages require manual intervention and work to optimize them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agree on release windows&lt;/strong&gt;. Before each release, notify all stakeholders: the team, the client, and the users. Define fixed time windows for applying changes to minimize risks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Always have a plan B&lt;/strong&gt;. Keep a rollback plan somewhere "under your pillow". If a release causes problems, who will fix them and how? Create a step-by-step checklist for handling such situations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Difficulties are opportunities for growth
&lt;/h2&gt;

&lt;p&gt;The role of a team leader is a balance between technology, people, and processes. The key is not to fear uncertainty. A systematic approach, open communication, and attention to detail will help you overcome any challenges. Don’t forget: the team leader is the one who leads.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Author: Maria Serebrovskaya - Team Lead at Byteminds&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
    </item>
  </channel>
</rss>
