<?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: Mánu Fosela</title>
    <description>The latest articles on DEV Community by Mánu Fosela (@manufosela).</description>
    <link>https://dev.to/manufosela</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F386088%2F2e07a2cb-dc32-4fb0-9b26-fe7d57550519.png</url>
      <title>DEV Community: Mánu Fosela</title>
      <link>https://dev.to/manufosela</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/manufosela"/>
    <language>en</language>
    <item>
      <title>The Hardest Part Isn't AI. It's Us</title>
      <dc:creator>Mánu Fosela</dc:creator>
      <pubDate>Mon, 17 Aug 2026 09:41:03 +0000</pubDate>
      <link>https://dev.to/manufosela/the-hardest-part-isnt-ai-its-us-bp9</link>
      <guid>https://dev.to/manufosela/the-hardest-part-isnt-ai-its-us-bp9</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/manufosela/the-shifting-role-of-the-software-engineer-4ph8"&gt;In the first article&lt;/a&gt; I talked about how AI is shifting our work toward a higher level of abstraction: increasingly less focused on writing the how and more on deciding what we want to achieve. &lt;a href="https://dev.to/manufosela/delegating-to-ai-means-governing-the-environment-ef3"&gt;In the second&lt;/a&gt;, I tried to answer the inevitable question: if we’re going to delegate more and more work to agents, how do we do it without having to blindly trust them? My answer was to build governance systems around them: external rules, verification mechanisms, guardrails and orchestration.&lt;/p&gt;

&lt;p&gt;In this one, I want to address something just as important, if not more so: how to lead this change. Because we can solve those two problems perfectly and still fail.&lt;/p&gt;

&lt;p&gt;Get comfortable, because this article is long.&lt;/p&gt;

&lt;p&gt;Companies don’t change when we install a tool, or when we adopt a different way of working. They change when the people working in them change and their culture, or part of it, changes too, not because the company loses its essence, vision, mission or values, but because it evolves. And this is where we probably enter the most complicated part of the game.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing code is starting to become the easy part
&lt;/h2&gt;

&lt;p&gt;Right now, and even more so in the future, it increasingly looks like “writing code” is going to be the easy part. Careful! I’m not saying that developing software is easy. Not even close. I’m saying exactly the opposite. Writing the code is becoming the part we can delegate most easily. The hard part is starting to move somewhere else.&lt;/p&gt;

&lt;p&gt;Understanding what needs to be done. Understanding the domain. Designing it. Choosing between alternatives. Knowing what consequences a decision might have. Measuring it. Testing it. Operating it. Maintaining it. Knowing whether it actually solves the problem we wanted to solve. And above all, being able to look at something an AI has built, verify that it works perfectly and say: “This works, but it’s wrong”. And even more importantly, being able to explain why.&lt;/p&gt;

&lt;p&gt;DISCLAIMER: the following example is there to illustrate what I mean in the simplest possible way. Software engineering scholars, please refrain from nitpicking it. Don’t look at the finger. Look at the moon.&lt;/p&gt;

&lt;p&gt;For example (JavaScript code). Imagine you come across this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isDigit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works. You give it a value and it tells you whether it’s a digit. It can perfectly pass every test we’ve defined.&lt;/p&gt;

&lt;p&gt;And yet, almost anyone with some experience will probably stare at it for a few seconds and think: “Do we really need ten ifs to express this?”.&lt;/p&gt;

&lt;p&gt;I’m sure that while you were reading it, you were already thinking of another way to do it. Maybe even several. And we’d probably end up debating which one is better.&lt;/p&gt;

&lt;p&gt;Well, that’s exactly what I’m talking about.&lt;/p&gt;

&lt;p&gt;Not that this function is going to break production or that it is objectively incorrect. It is deliberately a ridiculously simple example. I’m talking about that ability to look at a solution that works and ask yourself whether it is a good solution, whether it expresses properly what we want to do and whether there is a better way to solve it. A solution can satisfy every criterion we’ve checked and still be badly designed. It can do unnecessary work. It can introduce coupling we don’t need. It can add absurd complexity. It can introduce a decision today whose cost we won’t see until we have to modify, operate or maintain that part of the system. It can be insecure. It can work and still be shit.&lt;/p&gt;

&lt;p&gt;That’s judgment. And it’s something we’ve so often seen AI lack, precisely because it has been trained on all the code in the whole damn world, where we upload every kind of code imaginable.&lt;/p&gt;

&lt;p&gt;In the first article I said that our work is shifting upwards. I think this is one of the places it’s shifting to. And if that is going to become one of the main sources of value of a software engineer, another problem immediately appears: how do we help those who already have that judgment move it into this new way of working, and how do we train those who don’t have it yet? That is one of our biggest challenges. And it isn’t technological. It’s human.&lt;/p&gt;

&lt;h2&gt;
  
  
  “You’re taking away what I love doing: programming”
&lt;/h2&gt;

&lt;p&gt;Put yourself in the shoes of someone who has spent five, ten, fifteen or twenty years programming. Depending on when they started, they probably didn’t choose this profession only because it paid well, although we have lived through a “golden age” in which many people moved into software development because there was plenty of work and it paid well, and many of them also discovered their passion thanks to that. Most people who program do it because they enjoy programming. Not necessarily outside working hours too, although quite a few of us do ;)&lt;/p&gt;

&lt;p&gt;What we tend to have in common is that we enjoy facing a problem. Thinking of a solution. Writing a function that is efficient and elegant at the same time. Finding an abstraction. Removing twenty lines and turning them into five while somehow making the code more readable. Building something useful for someone else. Looking at something you’ve just finished and thinking: “I am God”. Let’s ignore those other occasions when we think we chose the wrong profession and are completely useless at this :P&lt;/p&gt;

&lt;p&gt;Then the first traces of AI appear. Autocomplete. Suggestions for methods, functions, classes. We start asking it for larger pieces, with varying degrees of success. We start using it to get past the blank page. To write tests for us. It finds bugs and saves us hours of debugging. But it’s still an assistant. A helper. Support.&lt;/p&gt;

&lt;p&gt;And then someone comes along telling you about the wonders of agentic development, that AI can write the code, and tells you that, if everything evolves the way it seems to be evolving, programming the way we have until now will gradually become less necessary. Well. I wouldn’t exactly expect people to jump for joy.&lt;/p&gt;

&lt;p&gt;I think one of the things we do wrong when talking about AI adoption is minimising this. We treat it as resistance to change. Irrational fear. People who “don’t want to adapt”. And part of it may be that. But another part isn’t. Mainly for two reasons: disappointing experiences with “the technologAI” and a very real sense of loss. The feeling of becoming a secondary actor. Of losing the thing you’re good at and that gives you identity. Even of becoming expendable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grief
&lt;/h2&gt;

&lt;p&gt;If you’ve spent years building a professional identity around something and that something starts to change, there is a kind of grief involved. I think we need to say it. Something is lost. Something dies a little bit inside our “soul”. Because in all likelihood we will gradually write less code manually. Many of the things that give us professional satisfaction today will slowly stop being part of our everyday work. Pretending that isn’t happening doesn’t help.&lt;/p&gt;

&lt;p&gt;But I don’t think craftsmanship disappears either. I think some of that craftsmanship also moves upwards and some of it, the more romantic part, will continue “weaving by hand” every line of code when efficiency, quality or the problem itself justify it. The pleasure of writing an especially efficient and readable line of code may turn into designing an especially efficient and scalable architecture. Finding a simple way to solve a complex problem. Defining an acceptance criterion that catches twenty incorrect solutions. Spotting a consequence nobody else had noticed. Being able to look at five proposals generated by agents and understand which one makes sense and why. Or even realising that we’re perfectly building something nobody actually needs.&lt;/p&gt;

&lt;p&gt;Most of us will gradually stop being craftspeople of code. But we may become another kind of craftsperson: of products, systems and impact. And that doesn’t happen automatically because someone gave us access to Claude or Codex. We have to help people make that transition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding doesn’t mean accepting that someone stands still
&lt;/h2&gt;

&lt;p&gt;This is where, for me, an idea I’ve tried to apply for years when leading people comes in. You can perfectly understand why someone is afraid, why they feel threatened, or why they don’t want to abandon a way of working they master or feel comfortable with. And at the same time clearly tell them that they need to explore it, consider the new paradigm and accompany them while they do it. Those positions aren’t contradictory. Understanding doesn’t mean lowering expectations. Neither does supporting someone.&lt;/p&gt;

&lt;p&gt;In the way I understand leadership, which I call affective leadership, I build relationships with the people I lead and put emotions at the same level as objective considerations, so I don’t avoid uncomfortable conversations. I initiate them when they are necessary, because allowing a problem to fester isn’t caring for someone either. If I believe someone may be left behind professionally, my responsibility isn’t to reassure them by saying nothing is going to change. It’s to help them prepare for the change.&lt;/p&gt;

&lt;p&gt;And I don’t think the answer is to tell someone “adapt or you’ll be left behind” either. That’s not leadership. That’s dumping the problem on them and leaving them alone. Leading this transition means explaining where I think we’re going, listening to where each person is, what they think and feel, understanding what they need and building a path so they can get there. But the destination can’t disappear from the conversation just because talking about it is uncomfortable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t evangelise by selling “magic”
&lt;/h2&gt;

&lt;p&gt;I also think there’s a common mistake among those of us who adopt these tools early. We get excited and start evangelising.&lt;/p&gt;

&lt;p&gt;“This is insane!”&lt;br&gt;
“I’m ten times faster”&lt;br&gt;
“I barely program anymore”&lt;br&gt;
“I did in one afternoon what used to take me a week”&lt;/p&gt;

&lt;p&gt;It may be true. I’ve experienced it myself. But then somebody tries exactly the same tool, has a horrible afternoon and thinks you’ve sold them bullshit. Because we forget the details. How many hours did you spend experimenting and failing before you got there? How many prompts, specs, skills, “MCPs”... did you try and discard? In other words, what price did you pay to get where you are? (pun very much intended)&lt;/p&gt;

&lt;p&gt;And there will be bad days too. The agent will stubbornly insist on some absurd solution. A model update will change something that worked. The context will be wrong. The provider will go down. Something will get through controls you thought were sufficient. None of that invalidates the direction. It means we’re working with a technology that is still evolving at a brutal pace.&lt;/p&gt;

&lt;p&gt;If we promise “magic”, every failure destroys trust. If we explain that we’re building a new way of working that still has problems, every failure can become an opportunity to learn. We don’t need believers. We need engineers experimenting, learning and building judgment around this new way of working. And we also need to understand that not everyone is at the same point. For someone who feels they’re training the thing that’s going to “take their job”, experimenting with all this can be extremely uncomfortable.&lt;/p&gt;

&lt;p&gt;You don’t leave the office on Friday as a code craftsperson and come back on Monday morning having changed professions. We’d be making a mistake if we thought someone who has spent fifteen years developing in a certain way is going to change because we install a tool, give a talk and write a “Notion” explaining that now we’re AI-first. The transition has to happen while working.&lt;/p&gt;

&lt;p&gt;First, without a doubt, we need to build the governance structure, which serves both AI and humans. An environment that guarantees nobody can screw things up. Then maybe you delegate the things that bring you the least value. Then a small implementation. Then a whole task. You start reviewing plans before the agent writes anything. You start worrying less about exactly how a particular function was implemented and more about whether the complete solution makes sense. Little by little, you stop being inside every line and start sitting above the system. And one day you realise you’ve spent hours developing software without writing much code at all, and that the code it generated meets all the requirements for quality, efficiency, security... just like your best code.&lt;/p&gt;

&lt;p&gt;That change shouldn’t be imposed as a surrender. We should help each person discover where their value lies now. A senior who has spent fifteen years programming has an enormous amount of knowledge and experience that isn’t stored in their fingers. It’s in all the shit they’ve been through. In those migrations that went wrong. In that wonderful architecture that six months later nobody knew how to maintain. In that optimisation nobody actually needed. In that ridiculous bug that reached production and nobody spotted. In that technically perfect product or feature that nobody used. That is exactly what we can’t afford to lose when we delegate writing code to AI. We need to apply that judgment to what AI produces.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem is that we have to learn while still delivering
&lt;/h2&gt;

&lt;p&gt;This is, for me, one of the most interesting organisational problems. We have to adopt a new way of developing software, but the company can’t stop developing software while we learn. We can’t say: “For the next six months we’re not shipping anything because we’re training everyone on AI”. We have to do both at the same time. Learn and deliver. Accepting that is no small thing.&lt;/p&gt;

&lt;p&gt;For a while, we may have to accept a percentage drop in productivity while trying to learn how to become dramatically more productive. And that’s something the company has to accept if it doesn’t want to be left behind. Someone will reasonably ask: and how do I know whether that bet is paying off? Not by the number of tokens we’re spending or the number of lines we’re writing. You start seeing it when more work reaches production with fewer errors. That’s the signal that we’re learning to become more productive, rather than merely faster. And for that, you should already be measuring the impact you have now so you can compare it with the impact you’ll have later.&lt;/p&gt;

&lt;p&gt;And I think platform engineering and developer experience teams play a fundamental role here. If your company doesn’t have them yet, you’re already late :P&lt;/p&gt;

&lt;p&gt;And during the process we’ll get things wrong. We’ll try tools that we abandon two weeks later. We’ll build processes that are too complicated. We’ll swing too far in the opposite direction. We’ll come back. And we’ll encounter another problem: the technology we’re trying to learn isn’t standing still. While we move forward step by step, models improve, tools change and agents acquire new capabilities. Something that was considered good practice six months ago — well, let’s say six weeks now — may stop making sense.&lt;/p&gt;

&lt;p&gt;That’s why I don’t think adoption can be based mainly on traditional training. We can’t think: course → certification → now you know AI. The unit of learning has to be the work itself. Real cases. Real projects. Real experiments. And mechanisms that allow what one person learns today to spread to the rest of the team tomorrow. Knowledge governance.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do we build judgment?
&lt;/h2&gt;

&lt;p&gt;And now we come back to what I said at the beginning. If writing code is becoming the easy part and a growing share of an engineer’s value is shifting toward judgment, a pretty f...reaking hard question appears: how do you acquire judgment? Because nobody is born knowing how to look at a solution and say “this works, but it’s wrong”.&lt;/p&gt;

&lt;p&gt;Traditionally we’ve acquired it by doing. Programming. Making mistakes. Seeing the consequences. Getting reviews. Maintaining our own decisions years later. Discovering why something that looked so elegant was actually stupid. Working alongside people who knew more than us. Putting in hours, coffees and pizzas. Debating. Doing hackathons.&lt;/p&gt;

&lt;p&gt;And then the inevitable question about juniors appears. How do we train juniors if traditionally a junior learned by programming, making mistakes, receiving reviews and accumulating experience until becoming senior, but now an AI is going to execute a large part of that work?&lt;/p&gt;

&lt;p&gt;I think we’re asking the wrong question. Because what turns someone into a senior isn’t having written enough lines of code. It’s having acquired judgment. A senior isn’t someone with five years of experience. It’s someone capable of avoiding a mistake before it happens and recognising a problem before it lands on top of them. Five, ten or fifteen years of just any kind of experience don’t make you senior. It’s a very particular kind of experience that hardens that layer of seniority, the kind that teaches you when a solution makes sense and when it doesn’t. What questions to ask. How to detect that something apparently correct doesn’t fit. To understand that every decision has a price, what you gain and what you give up. To understand that there are almost never silver bullets capable of killing the “werewolf”.&lt;/p&gt;

&lt;p&gt;And so the interesting question becomes a different one: how does a junior acquire judgment if AI is executing a large part of the work? I think the answer is right in front of us and we’re not seeing it. We can’t see the forest for the trees.&lt;/p&gt;

&lt;h2&gt;
  
  
  The washing machine
&lt;/h2&gt;

&lt;p&gt;This happened to me during these holidays with a washing machine. We were staying in an apartment and there was a fairly analogue washing machine from a brand I didn’t know, with programs numbered from 1 to 8. I needed to use it. Program 3? 5? 7? No idea. So I did what many of us would probably do today. I took a photo and sent it to ChatGPT. It recognised the washing machine and the model. It asked what I wanted to wash, I explained what clothes were in the load and it recommended a program.&lt;/p&gt;

&lt;p&gt;If I’d been alone, I would have used that program. No further questions. Why? Because I’m the junior when it comes to laundry in our house. Generally speaking, I handle cooking and shopping. My wife handles the laundry. So I lack mileage, in other words, I don’t have judgment. I have some knowledge: I know the basics of washing programs, separating clothes, adding fabric softener and which one we prefer... But I lack judgment. Standing in front of that washing machine, I didn’t have enough experience.&lt;/p&gt;

&lt;p&gt;Fortunately, my wife was there, and in that domain she’s the senior. And she didn’t simply accept the first answer. She said:&lt;/p&gt;

&lt;p&gt;“But I want the clothes to come out like this. Did you tell it that?”&lt;br&gt;
“How long does that program take? Wow! That seems like a lot”&lt;br&gt;
“What temperature does it wash at?”&lt;br&gt;
“Do you know the water here is really hard?”&lt;/p&gt;

&lt;p&gt;She questioned the solution. Added context. Made me debate with the model. Compared its answer with what she knew from experience. From having “broken” laundry loads. And eventually, we arrived at another program that fitted what we actually wanted better. And the clothes came out perfectly!&lt;/p&gt;

&lt;p&gt;That was the difference between junior and senior. I had an answer. She had the judgment to evaluate the answer.&lt;/p&gt;

&lt;p&gt;Now imagine she hadn’t been there. I would have used the first program ChatGPT recommended, taken the clothes out and thought: “This hasn’t come out the way I expected”. What would I have done next? Gone back to ChatGPT. Explained what happened. Asked why. It would agree with me and apologise, obviously. We’d compare alternatives. And probably on the next wash, or twenty washes later, we would eventually have reached the same conclusion my wife arrived at by asking a few questions. With one small difference. I would have had to break a few laundry loads to learn it. And after enough loads, I would have had more judgment than before.&lt;/p&gt;

&lt;p&gt;Well, I think that’s how we need to train juniors too. We need to let them do laundry. A junior needs to do things. They need to make decisions. They need to make mistakes. They need to see the consequences. They need to ask AI, try its answer and discover that sometimes something that sounded perfectly reasonable doesn’t produce exactly the result they expected.&lt;/p&gt;

&lt;p&gt;We need to let them use AI from day one. Not teach them for years how to work the way we did in the pre-AI era, only to then explain how to stop working that way. That’s absurd. It makes no sense.&lt;/p&gt;

&lt;p&gt;How many times have we seen the joke “another day without ever using the least common multiple”. And although we know that some knowledge and learning can appear useless while still helping us “furnish the mind”, we need to find the balance, carefully select what is useful and what will simply leave people feeling that they’ve learned something they’ll never use. We should be able to justify every thing we teach without AI, even when right now it looks like “wax on, wax off” (The Karate Kid, 1984) and they don’t understand why; soon enough they should be able to see that they’ve internalised useful things that help them build judgment. For example, learning how to really debug: reading a stack trace, forming a hypothesis about why something is failing and testing that hypothesis. Today you can ask an agent to do that too, but that is exactly the muscle you’ll later use to judge whether what AI did is actually right or merely looks right. It’s pure “wax on, wax off”: it seems useless until the day you look at a solution with all the tests green and know it’s wrong.&lt;/p&gt;

&lt;p&gt;And we need to teach them how to work with AI. But that doesn’t mean leaving them alone in front of an agent. It means teaching them to ask better questions. To challenge answers. To request alternatives. To understand why one option is better than another. To spot missing information. To verify the result. To debate and learn alongside them. And above all, let them break laundry loads. In environments where breaking a laundry load is cheap. Controlled projects. Scoped tasks. Development environments. Systems where mistakes are detected early. With people around them who have more judgment and can help them understand not only that something is wrong, but why.&lt;/p&gt;

&lt;p&gt;The governance system I talked about in the previous article also serves this purpose: allowing us to make mistakes without every mistake ending up in production. It doesn’t just protect the software. It can also become a learning environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  They also need to understand the rules
&lt;/h2&gt;

&lt;p&gt;There’s another source of learning that I think is particularly important. If we’re going to build systems where AI works under certain rules, juniors should know those rules. Not simply obey them. Understand them. Why do we do TDD? Why have we decided that a review should be performed by a model from another family? Why do we limit the size of a PR? Why was this architectural decision made this way? Why this dependency and not that one? What alternatives were there? What do we gain? What do we sacrifice? And we should debate those things with them and let them propose and experiment with their own alternatives alongside us.&lt;/p&gt;

&lt;p&gt;Because those rules are, to a large extent, accumulated judgment. They’re decisions made after years of mistakes, experiences and lessons learned. Explaining them means transferring part of that judgment. But we also need to explain something even more important: those rules may be wrong. They’re not commandments. They need to be questioned. They are decisions made in a particular context at a particular moment. And context changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Maybe that’s exactly why we need juniors more than ever
&lt;/h2&gt;

&lt;p&gt;There’s an interesting paradox here. We could think that if AI can do much of the work we traditionally assigned to junior profiles, we’ll need fewer juniors. I think that’s a huuuuuuge mistake. Because if we stop bringing juniors in, the obvious question is: where are the seniors going to come from in five or ten years?&lt;/p&gt;

&lt;p&gt;But there’s another reason. The juniors we train now may have an advantage that we seniors don’t have, and I’ve already seen this with my own children, who are moving toward the industry. We accumulate judgment. But we also accumulate habits. Ways of doing things. Decisions that worked for years. And a dangerous tendency to think: “We’ve always done it this way because it works”. Seniors have a tendency to rest on their laurels. And at the speed things are moving now, you can’t even afford a quick nap. In the past, that might have been relatively cheap. Technology could change a lot, but certain paradigms could last for years. Now some things change practically month by month.&lt;/p&gt;

&lt;p&gt;And this is really fucking hard. We’ve always said you need to stay up to date, but what’s happening now is frantic. In a way, we’ve all become “juniors” again: learning, experimenting and questioning continuously. Eternal juniors. No rest for the warrior.&lt;/p&gt;

&lt;p&gt;The people who are starting now and those who come after them, who learn from the beginning by working with AI and who we teach to build judgment, may be precisely the ones best positioned to question our own rules:&lt;/p&gt;

&lt;p&gt;“Why do we do this this way?”&lt;br&gt;
“Does this restriction still make sense?”&lt;br&gt;
“This model can now do something it couldn’t do before”&lt;br&gt;
“Why are we still keeping this manual step?”&lt;/p&gt;

&lt;p&gt;And maybe they’re right. That’s why we need to teach them our rules. But we also need to teach them to question those rules.&lt;/p&gt;

&lt;p&gt;That’s why we need juniors. Always. And that’s why I think companies should deliberately continue bringing juniors in and training them. Spend resources on it. Take vocational training and university interns and later bring them into the company. Create internal onboarding and training programmes. Give them projects where they can work inside an AI governance environment from day one, accompanied by people with more judgment and inside systems where making mistakes is cheap. We don’t need juniors to do the boring tasks seniors don’t want to do. AI will increasingly do those better. We need them for something much more important. To build the next generation of people with judgment. Because if the work shifts upwards, we also need to teach the next generation how to work up there.&lt;/p&gt;

&lt;p&gt;I’m genuinely worried about building an entire system where AI writes the code and eventually having nobody left capable of governing that AI and applying the necessary judgment. So let’s burn this into our brains: judgment isn’t installed. It’s built. Question by question. Decision by decision. Mistake by mistake. Laundry load by laundry load.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not everyone will move at the same speed
&lt;/h2&gt;

&lt;p&gt;Not everyone will move at the same speed, and we need to accept that there will be differences. Some people have already been working this way for months. Others are just starting. Someone will remain sceptical until they see data. Someone else will move too fast and we’ll have to hold them back. Another person will discover a way of working nobody had anticipated. We don’t need everyone to move at exactly the same pace. We need everyone moving in the same direction. Diversity with a shared goal.&lt;/p&gt;

&lt;p&gt;And we need to turn individual learning into team learning. Because in a transition this fast, we can’t depend on three people who independently figured out how to work well with these tools. What one person learns has to spread through the rest of the team. And tomorrow we need to be willing to throw part of that learning away because the conditions have changed again.&lt;/p&gt;

&lt;h2&gt;
  
  
  And the challenge also changes for those of us who lead
&lt;/h2&gt;

&lt;p&gt;All of this changes what those of us leading engineering teams need to do too. Buying licences isn’t enough. Saying we’re AI-first isn’t enough. Measuring how many developers use Copilot or how many tokens they spend isn’t enough.&lt;/p&gt;

&lt;p&gt;We need to help each person understand where their value is shifting. For someone who has spent fifteen years writing code, we need to help them understand that their experience doesn’t disappear just because someone else writes the lines: we need precisely that experience to judge what the agents produce. And for someone who doesn’t yet have that experience, we need to create opportunities to build it. We need to create spaces where experimentation is cheap. Allow bad days. Share what we’ve learned. Prevent the person moving fastest from looking down on someone who’s only just getting started, and also prevent someone who refuses to move from turning their fear into a reason that paralyses other people or the company itself, and make them aware when that is happening.&lt;/p&gt;

&lt;p&gt;We’ll also have to change what we recognise and reward. If we continue considering the best engineer to be the person who produces the most code while at the same time asking agents to produce the code, we’ve created a completely contradictory system. And please don’t measure AI adoption by who spends the most tokens. No. Maybe we need to start recognising much more the person who realised something shouldn’t be built at all. The person who prevented a bad architecture. The person who found a vulnerability. The person who got an agent to solve a difficult problem well while spending very few tokens. The person who taught five colleagues something they’d learned. The person who was able to explain why a solution that apparently worked was actually wrong. That’s engineering too. It probably always was. Writing code simply took up so much space that we couldn’t see it.&lt;/p&gt;

&lt;h2&gt;
  
  
  And yes: standing still isn’t an option either
&lt;/h2&gt;

&lt;p&gt;This is probably the most uncomfortable conversation. Not adopting these tools doesn’t mean preserving your current position, because the rest of the world doesn’t stand still with you. The bar moves. And there is also a generation coming for whom this transition won’t even exist. A developer starting a few years from now probably won’t have to “learn how to develop with AI”: it will simply be how they learned to develop. Just as someone who started using Git never had to experience the transition from copying folders called project_final, project_final_good, project_final_now_really_final... For them, version control simply existed. Something similar will happen with agents.&lt;/p&gt;

&lt;p&gt;I don’t think we should use that as a threat. But we shouldn’t hide it just to avoid an uncomfortable conversation either. Leadership also means explaining that the world is changing before the change runs you over.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary and conclusions
&lt;/h2&gt;

&lt;p&gt;Over the last few years, we’ve talked endlessly about AI as a productivity tool. How to write code faster. How to build more features. How to produce more with less. That’s already becoming too narrow. The real opportunity is much bigger: it’s about changing which part of the process machines do and which part we do.&lt;/p&gt;

&lt;p&gt;And if that happens, writing code will increasingly become less of what defines a software engineer, because writing code is starting to become “the easy part”. The hard part will be knowing which code deserves to exist. Understanding the problem. Designing the system. Knowing the domain. Choosing between alternatives. Measuring. Verifying. Understanding the consequences. Keeping it alive. And being able to look at something generated by AI, see that it compiles, that the tests are green, that it apparently does exactly what you asked it to do and say: “No. This works, but it’s wrong”. That’s judgment.&lt;/p&gt;

&lt;p&gt;And I think one of our biggest challenges over the next few years won’t be getting our teams to learn how to use AI. It will be helping those who already have that judgment move it to this new level, while making sure those who don’t have it yet can build it in a world where they will probably write less and less code themselves.&lt;/p&gt;

&lt;p&gt;AI can write the code. We need to develop the people capable of knowing what is worth building and when what has been built is wrong. And that has to be led, always with affection.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Delegating to AI Means Governing the Environment</title>
      <dc:creator>Mánu Fosela</dc:creator>
      <pubDate>Thu, 13 Aug 2026 15:54:17 +0000</pubDate>
      <link>https://dev.to/manufosela/delegating-to-ai-means-governing-the-environment-ef3</link>
      <guid>https://dev.to/manufosela/delegating-to-ai-means-governing-the-environment-ef3</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/manufosela/the-shifting-role-of-the-software-engineer-4ph8"&gt;previous article&lt;/a&gt;, I argued that AI isn't simply changing the tools we use to develop software, but shifting our work to a new level of abstraction. In this one, I want to address the problem that immediately follows: if we're going to write less and less code directly and agents are going to produce an increasingly larger part of it, how the hell do we know whether what they code is actually right?&lt;/p&gt;

&lt;p&gt;Because the answer obviously can't be “trust the AI, it's very smart”.&lt;/p&gt;

&lt;p&gt;Even though I personally develop code with AI today with practically no review, I don't blindly trust AI. Just as I don't blindly trust an engineer on my team. I don't even blindly trust myself.&lt;/p&gt;

&lt;p&gt;Blind trust is a security hole. And not blindly trusting someone doesn't mean distrusting them, it means having mechanisms to prevent their mistakes, or mine, from causing problems.&lt;/p&gt;

&lt;p&gt;That's why we've spent decades building mechanisms and methodologies around software development to detect, and avoid as much as possible, our mistakes. XP. Scrum. Tests. Code reviews. Pair Programming. CI. Static analysis. Permissions. Observability. Environments. Containers. Auditing...&lt;/p&gt;

&lt;p&gt;The question, therefore, shouldn't be whether we can trust an AI. The question should be &lt;strong&gt;what system do we need to build so we can use it without needing to blindly trust it?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  It's not deterministic
&lt;/h2&gt;

&lt;p&gt;One of the first objections is usually that if you ask it the same thing twice, it generates two different pieces of code.&lt;/p&gt;

&lt;p&gt;True.&lt;/p&gt;

&lt;p&gt;But if you give the same task to two different programmers, or to the same programmer with enough time in between, we'll very probably get two different implementations too, depending on the complexity of what we're asking.&lt;/p&gt;

&lt;p&gt;And if we've never required two developers to produce exactly the same code, why do we expect AI to produce exactly the same code from the same request?&lt;/p&gt;

&lt;p&gt;Isn't it enough for the result to satisfy the requested requirements?&lt;/p&gt;

&lt;p&gt;That it does what it's supposed to do. That it passes all kinds of tests. That it satisfies the acceptance criteria. That it meets security requirements. That it respects certain constraints.&lt;/p&gt;

&lt;p&gt;The problem appears when we keep evaluating the &lt;strong&gt;writing process&lt;/strong&gt; while this new level of abstraction increasingly forces us to evaluate the &lt;strong&gt;result against a contract&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And when I say contract, I'm not talking about a huge &lt;em&gt;spec&lt;/em&gt; telling the agent how it has to build every single thing. I'm talking about the conditions the result must satisfy and having mechanisms that can verify them.&lt;/p&gt;

&lt;p&gt;We haven't reached compiler-level reliability yet, and I don't know whether we ever will, or even whether we need to. What is happening is that agents are becoming more capable and we're giving them more and more autonomy.&lt;/p&gt;

&lt;p&gt;And the more autonomous they become, the less sense it makes to think we'll be able to control them by constantly looking over their shoulder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rules can't live inside the agent
&lt;/h2&gt;

&lt;p&gt;This is, for me, one of the important parts.&lt;/p&gt;

&lt;p&gt;A rule written only in a prompt isn't a rule. It's a request.&lt;/p&gt;

&lt;p&gt;You can have an &lt;code&gt;AGENTS.md&lt;/code&gt;, a skill or whatever you want telling it never to do a certain thing. It helps enormously. I use them, obviously. But we've already seen that the agent forgets them more often than we'd like, sometimes interprets them incorrectly, can even lose that information among the ever-growing amount of context it's handling, or simply finds another way to achieve what it was trying to do.&lt;/p&gt;

&lt;p&gt;And that last one isn't theoretical.&lt;/p&gt;

&lt;p&gt;While experimenting with all this, I configured a mini PC with Ubuntu to run a model's CLI. I removed the &lt;code&gt;rm&lt;/code&gt; command from the system so it wouldn't be able to delete anything.&lt;/p&gt;

&lt;p&gt;Then one day I discovered that it had deleted files. How? I couldn't believe it. I asked, and it explained that since &lt;code&gt;rm&lt;/code&gt; didn't work, it had used &lt;code&gt;rsync --delete&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I was surprised and scared at the same time.&lt;/p&gt;

&lt;p&gt;I had prevented it from using &lt;code&gt;rm&lt;/code&gt;. And even though I had explicitly told it never to delete a file without my approval, at one point it realized it had made a mistake and needed to rebuild a structure, so it decided it needed to delete things. It interpreted my prohibition as meaning it shouldn't delete anything critical and that, given the context it had, it could decide that deleting those files wasn't going to cause any problem. After all, it was code that hadn't existed before and that it had created itself, right?&lt;/p&gt;

&lt;p&gt;It disobeyed an instruction in order to achieve its goal.&lt;/p&gt;

&lt;p&gt;That's why important restrictions have to live outside the agent, in mechanisms it doesn't control. Hooks. CI. Permissions. Tests. Mutation tests. Policies. Gates...&lt;/p&gt;

&lt;p&gt;If I want every change to have tests, I can tell it to do TDD. But I also have to verify that those tests exist, that they run and that they actually pass.&lt;/p&gt;

&lt;p&gt;If I don't want any change to get in without review, I can't let the same agent decide that “this time” the review isn't necessary.&lt;/p&gt;

&lt;p&gt;If I want a security vulnerability to block a change, no second agent should be able to casually decide “well, it doesn't look that serious”.&lt;/p&gt;

&lt;p&gt;If I want all work to be traceable, it can't depend on an agent remembering to create a ticket afterwards.&lt;/p&gt;

&lt;p&gt;Important rules have to become mechanisms.&lt;/p&gt;

&lt;p&gt;Not good intentions.&lt;/p&gt;

&lt;p&gt;The agent can decide how to solve the problem. What it shouldn't be able to decide is whether it can bypass the mechanisms that determine whether that solution is acceptable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Orchestrating isn't governing
&lt;/h2&gt;

&lt;p&gt;And here I think we're usually mixing two different problems.&lt;/p&gt;

&lt;p&gt;One is how we &lt;strong&gt;orchestrate agents&lt;/strong&gt;. Who writes. Who researches. Who reviews. How we share context. How several agents work in parallel. Which model we choose for each task.&lt;/p&gt;

&lt;p&gt;We can have one writing, another from a different family reviewing, and a third arbitrating when they disagree. We can even launch the same task to several models and then choose between the solutions that satisfy our conditions.&lt;/p&gt;

&lt;p&gt;This already looks much more like building an artificial team than using a programming assistant.&lt;/p&gt;

&lt;p&gt;But there's another different problem: &lt;strong&gt;who governs those agents?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Who establishes the rules? Who decides which actions are allowed? Who checks that they were actually followed? Who prevents an agent from granting itself an exception?&lt;/p&gt;

&lt;p&gt;Orchestration gives capability.&lt;/p&gt;

&lt;p&gt;Governance gives guarantees.&lt;/p&gt;

&lt;p&gt;And we need both.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about specs?
&lt;/h2&gt;

&lt;p&gt;For a while it seemed that the answer to AI development was the prompt engineer.&lt;/p&gt;

&lt;p&gt;It lasted months.&lt;/p&gt;

&lt;p&gt;Then the idea emerged that development with agents was going to be about writing better specs. Right?&lt;/p&gt;

&lt;p&gt;If the AI made mistakes, it was because we hadn't specified clearly enough what we wanted. So we started writing increasingly detailed documents explaining which endpoints to create, which classes to use, which structure to follow, which cases to handle...&lt;/p&gt;

&lt;p&gt;I think that's also starting to become “the past”.&lt;/p&gt;

&lt;p&gt;If today I tell a good frontier model “build me a REST API to access my recipe database”, and it has access to the repository, the database and the project context, I don't need to explain that I probably need to list recipes, retrieve one, create one, modify one or delete one, or how to do any of that.&lt;/p&gt;

&lt;p&gt;It will explore. It will understand the schema. It will look at how the rest of the project is built, its stack, its style, the conventions we use, even previous conversations we've had, and it can make many of those decisions by itself surprisingly well.&lt;/p&gt;

&lt;p&gt;If I've also told it that we work with TDD, that APIs must be documented and that it should generate a Postman collection, I don't have to repeat that in every request either.&lt;/p&gt;

&lt;p&gt;But, and here we go back to the beginning of the article, telling it something is one thing and guaranteeing that it does it is another.&lt;/p&gt;

&lt;p&gt;Instructions guide.&lt;/p&gt;

&lt;p&gt;The environment governs.&lt;/p&gt;

&lt;p&gt;That changes where we put our effort again.&lt;/p&gt;

&lt;p&gt;I don't need to translate my need into an almost technical specification first so that the AI can then translate that specification into code.&lt;/p&gt;

&lt;p&gt;I can express the intention at the highest possible level, my need, my desire, my idea, nothing more: “I want to access my recipes through a REST API”.&lt;/p&gt;

&lt;p&gt;It's practically the same thing I do as a manager when I ask a senior engineer. I don't have to specify the details, because they know how we work, they know the conventions, the rules and the context.&lt;/p&gt;

&lt;p&gt;The detailed &lt;em&gt;spec&lt;/em&gt; was, in a way, another layer of human translation between the idea and the code. And AI is starting to eat that layer too.&lt;/p&gt;

&lt;p&gt;What doesn't disappear, and actually becomes more important, are the rules, the constraints and the definition of what really matters.&lt;/p&gt;

&lt;p&gt;Are we moving to RDD? Rules-Driven Development? Are they going to start selling us courses on how to govern agents?&lt;/p&gt;

&lt;p&gt;Of course 😝&lt;/p&gt;

&lt;p&gt;Jokes aside, one thing is telling the agent exactly how to build the solution, which is something I don't do with a senior engineer either, and another very different thing is giving it enough context to make good decisions. A senior engineer can work from a high-level request because they know the domain, understand the product, know how the team works and have references they can use to compare their decisions. And if they don't know something, they know where to look or who to ask.&lt;/p&gt;

&lt;p&gt;The same should happen with an agent. If it has access to the domain, the code, the documentation, previous decisions, examples and the team's rules, I increasingly need less to turn my need into a detailed implementation spec.&lt;/p&gt;

&lt;p&gt;What is still necessary is defining the conditions that any solution must satisfy. Those can live in team agreements, a playbook, a developer kit, ADRs, architecture rules, security criteria or verification mechanisms.&lt;/p&gt;

&lt;p&gt;Fewer specs doesn't mean less context. It means stopping using the spec as a manual translation layer between the need and the implementation.&lt;/p&gt;

&lt;p&gt;That's where we're getting to, if we're not already there.&lt;/p&gt;

&lt;p&gt;Less implementation specification. More intention. More available context. More mechanisms around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Governance also has a cost
&lt;/h2&gt;

&lt;p&gt;But here's another problem I don't think we've solved yet.&lt;/p&gt;

&lt;p&gt;Guardrails work, but many times they work &lt;strong&gt;after the agent has already smashed into them&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It already happens to me.&lt;/p&gt;

&lt;p&gt;One of my rules may say that I don't want PRs larger than 200 lines.&lt;/p&gt;

&lt;p&gt;The agent knows it.&lt;/p&gt;

&lt;p&gt;And even so, far too often, it ends up trying to create a PR with more than 200 lines.&lt;/p&gt;

&lt;p&gt;The hook blocks it.&lt;/p&gt;

&lt;p&gt;Perfect.&lt;/p&gt;

&lt;p&gt;Governance worked.&lt;/p&gt;

&lt;p&gt;But now the agent has to go back. Redo part of the work. Split it. Maybe create another file. Reorganize imports. Divide the PR. Run things again.&lt;/p&gt;

&lt;p&gt;And all of that consumes tokens.&lt;/p&gt;

&lt;p&gt;A lot of them.&lt;/p&gt;

&lt;p&gt;The final result may comply perfectly with the rules, but we got there in an obviously inefficient way.&lt;/p&gt;

&lt;p&gt;The system is safe, but the agent has been banging its head against the walls until it found the right path.&lt;/p&gt;

&lt;p&gt;And this happens because the rules that live in the model's context don't have the same weight as the real restrictions that live outside it. The model may “know” that a rule exists and still forget it during execution.&lt;/p&gt;

&lt;p&gt;I use tools to try to reduce token consumption, such as RTK or Squeezr, but you're only optimizing one part of the problem.&lt;/p&gt;

&lt;p&gt;The underlying problem is still there.&lt;/p&gt;

&lt;p&gt;Building better guardrails isn't enough. We need agents to get better at understanding and maintaining those constraints throughout the work so they don't waste time, tokens and money attempting solutions that we already know the system is going to reject.&lt;/p&gt;

&lt;p&gt;I think this is one of the areas where companies building models and agents have a huge amount of room for improvement.&lt;/p&gt;

&lt;p&gt;Because fully delegating development doesn't just require the result to be correct.&lt;/p&gt;

&lt;p&gt;Getting there also has to be reasonably efficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do we have to do everything with frontier models?
&lt;/h2&gt;

&lt;p&gt;And this efficiency problem leads me to something else.&lt;/p&gt;

&lt;p&gt;If we're going to have agents working, reviewing, arbitrating, retrying after hitting a guardrail and handling increasingly large amounts of context, always using the most powerful frontier model for absolutely everything doesn't seem particularly smart either.&lt;/p&gt;

&lt;p&gt;Or cheap.&lt;/p&gt;

&lt;p&gt;I think companies should seriously start considering running their own models in their infrastructure too.&lt;/p&gt;

&lt;p&gt;And individuals as well.&lt;/p&gt;

&lt;p&gt;Not necessarily to replace Claude, Codex or whatever frontier model happens to be the one next week. But to complement them.&lt;/p&gt;

&lt;p&gt;There are tasks a local model can do well enough. Repetitive tasks. Classification. Context retrieval. Some reviews. Simple generation. Work involving code that, for security reasons, you don't want leaving your infrastructure.&lt;/p&gt;

&lt;p&gt;And when the task requires more capability, when the local model can't handle it, when it gets stuck or when you need someone to come to the rescue, you escalate to a frontier model.&lt;/p&gt;

&lt;p&gt;Just as I wouldn't put my best engineer on absolutely every task in the team simply because they're the best engineer.&lt;/p&gt;

&lt;p&gt;We have to find the balance.&lt;/p&gt;

&lt;p&gt;Which model we use for what. What stays inside. What goes outside. When it's worth paying more and when a smaller model is enough.&lt;/p&gt;

&lt;p&gt;And this reinforces again the idea that we're not heading towards “using an AI”.&lt;/p&gt;

&lt;p&gt;We're heading towards building &lt;strong&gt;teams of AIs&lt;/strong&gt;, each one doing what makes the most sense for it, under a common set of rules that none of them controls.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I see this governance?
&lt;/h2&gt;

&lt;p&gt;After thinking about it a lot, experimenting and taking a few hits along the way, I more or less know what my ideal framework looks like for developing software with AI without having to blindly trust it, but still having enough governance guarantees.&lt;/p&gt;

&lt;p&gt;I want to start by telling the model what I'm trying to do, at the highest level possible. My need, my idea, the problem I want to solve. Sometimes I may even give it a pile of documents that have been written about the idea, for example, a role-playing game with its rules, its objective, its cards... And once I give it that high-level instruction and some details, I want it to generate a first plan.&lt;/p&gt;

&lt;p&gt;That plan I do want to review. Me as an engineer, other engineers if necessary, or even other agents from different families that challenge it, look for problems, suggest alternatives and debate the decisions with it.&lt;/p&gt;

&lt;p&gt;Once the plan is approved, I want the system itself to turn it into an actual working plan. Epics. Tasks. Dependencies. Estimates. Acceptance tests. Everything initially generated by AI, but something we can discuss, correct and complete with it before starting.&lt;/p&gt;

&lt;p&gt;In other words, I don't want to write a huge &lt;em&gt;spec&lt;/em&gt; myself telling it how to do things. I want to express what I need and discuss with the AI the plan it proposes to achieve it.&lt;/p&gt;

&lt;p&gt;And once that plan is approved, then yes, let the machinery start.&lt;/p&gt;

&lt;p&gt;Let an orchestration of agents pick up the tasks. Let them architect when architecture is needed. Let them code. Let other agents review. Let them debate when they disagree. Let us pit several models against the same problem and run tournaments if it makes sense. Let there be specific security reviews. Let tests, mutation tests, static analysis, SonarQube or SonarCloud, linters and all the tooling we consider necessary run.&lt;/p&gt;

&lt;p&gt;But all of that inside an environment that doesn't depend on the agent remembering our rules.&lt;/p&gt;

&lt;p&gt;Pre-commit and pre-push hooks. CI gates. Permissions. Real limits. Policies that prevent certain things from happening even if the agent wants to do them.&lt;/p&gt;

&lt;p&gt;The agent can decide how to get there.&lt;/p&gt;

&lt;p&gt;The system decides where it is allowed to go.&lt;/p&gt;

&lt;p&gt;And I want incremental updates on every merge to &lt;code&gt;main&lt;/code&gt; to keep the codebase RAG up to date, plus a full reindex on every release so that when we deploy software, the system's own knowledge is refreshed as well. And also update the wiki and the documentation so that the next agent that arrives isn't working from how the system looked three months ago, but from how it looks now.&lt;/p&gt;

&lt;p&gt;And that RAG, that wiki, that task board, all of it should be inspectable by the engineer, so it can be corrected or understood.&lt;/p&gt;

&lt;p&gt;For me, that's the framework.&lt;/p&gt;

&lt;p&gt;Human intention. A generated and debated plan. Decomposed and verifiable work. Teams of specialized agents executing it. Other agents reviewing and challenging it. Deterministic tools checking it. External guardrails preventing them from stepping outside the rules. And knowledge updated after every change.&lt;/p&gt;

&lt;p&gt;Not trusting that the agent will do it right.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building an environment where it's very difficult for it to do it wrong and even harder for an error to reach production.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And even then, it will make mistakes.&lt;/p&gt;

&lt;p&gt;That's why we need to build a governance system for our agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  The compiler from decades ago
&lt;/h2&gt;

&lt;p&gt;Today's AI still isn't that modern compiler we trust without practically ever looking at what it generates.&lt;/p&gt;

&lt;p&gt;It's more like the compiler from decades ago. Good enough to change the profession. Not good enough to blindly trust.&lt;/p&gt;

&lt;p&gt;And that's precisely why I think we're making a mistake when we conclude that we should keep manually reviewing every line.&lt;/p&gt;

&lt;p&gt;The answer should be something else: &lt;strong&gt;learning to build better systems around it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Systems that limit. And verify. And block. That allow mistakes without causing a disaster.&lt;/p&gt;

&lt;p&gt;But also systems that are efficient and don't force the agent to discover the rules by smashing into them again and again.&lt;/p&gt;

&lt;p&gt;Because the goal shouldn't be to make AI never make mistakes.&lt;/p&gt;

&lt;p&gt;We can't even achieve that with humans. I wish.&lt;/p&gt;

&lt;p&gt;The goal should be that when it makes a mistake, the system detects it before it matters. And, if possible, before it burns through a few million tokens discovering it.&lt;/p&gt;

&lt;p&gt;And even if we solved all of this, we'd still be left with probably the hardest problem.&lt;/p&gt;

&lt;p&gt;Getting an engineer to actually want to work this way.&lt;/p&gt;

&lt;p&gt;Because changing the tools is one thing.&lt;/p&gt;

&lt;p&gt;And telling someone who has spent years programming that part of what defines their profession, and something they probably love doing, is no longer going to be done by them is something very different.&lt;/p&gt;

&lt;p&gt;And CI doesn't fix that.&lt;/p&gt;

&lt;p&gt;This new path has to be led.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>softwareengineering</category>
      <category>testing</category>
    </item>
    <item>
      <title>The Shifting Role of the Software Engineer</title>
      <dc:creator>Mánu Fosela</dc:creator>
      <pubDate>Tue, 11 Aug 2026 09:43:26 +0000</pubDate>
      <link>https://dev.to/manufosela/the-shifting-role-of-the-software-engineer-4ph8</link>
      <guid>https://dev.to/manufosela/the-shifting-role-of-the-software-engineer-4ph8</guid>
      <description>&lt;p&gt;This is the first of three articles about how I see AI adoption in software development within companies and how I'm pushing it forward at TRIBBU, my company.&lt;/p&gt;

&lt;p&gt;First, I want to talk about the change itself: what's happening to our work and why I think we're facing a change in the level of abstraction. In the second one I'll talk about how we can govern software development carried out by agents. And in the third, probably the most complicated one, about how to lead people through this transition.&lt;/p&gt;

&lt;p&gt;Because technology will probably be the easy part.&lt;/p&gt;

&lt;p&gt;We're living through a “turbulent” yet exciting period with the arrival of what we somewhat incorrectly call artificial intelligence into our lives.&lt;/p&gt;

&lt;p&gt;Although I think software development is where its adoption has gone deepest, little by little it's making its way into every discipline and more and more people are using it to make their day-to-day work easier and better.&lt;/p&gt;

&lt;p&gt;It's true that some people use it to do their work for them. Often badly, especially when that work requires judgment.&lt;/p&gt;

&lt;p&gt;And it's also true that if an AI can do your work because that work doesn't require judgment, then your job will, sooner or later, be replaced by AI.&lt;/p&gt;

&lt;p&gt;But that's not what I want to talk about.&lt;/p&gt;

&lt;p&gt;I want to focus on how to lead the transition towards software development with AI inside companies.&lt;/p&gt;

&lt;p&gt;And here I see two different problems.&lt;/p&gt;

&lt;p&gt;One is how to “evangelize”, more than convince, how to get them to convince themselves, to “convert”, and get developers to see the enormous benefits it can bring, leaving behind bad past experiences caused by using it without judgment or knowledge.&lt;/p&gt;

&lt;p&gt;The other one, much more complicated in my opinion, is &lt;strong&gt;how to do it well&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Because right now there's a lot of ignorance, very little accumulated experience and, above all, a huge disparity of opinion about how far we're willing to stop doing what until now we considered our job: writing code.&lt;/p&gt;

&lt;p&gt;I'm going to address both problems, but I want to start with the how.&lt;/p&gt;

&lt;p&gt;Because I think that if we can answer the how properly, solving the first problem will be much easier.&lt;/p&gt;

&lt;p&gt;It will be easier for a programmer to accept delegating an increasing part of their work to an AI if they can trust the system through which they do it.&lt;/p&gt;

&lt;p&gt;Because let's not fool ourselves: feeling that something artificial is beginning to do what you've spent years doing, what you're good at and, even worse, what you probably love doing, is hard. Very hard.&lt;/p&gt;

&lt;p&gt;But denying the evidence, what's happening, won't make it stop happening. Let's not be like little children who cover their faces and think nobody can see them.&lt;/p&gt;

&lt;p&gt;And let's be aware that an AI-native generation is taking shape, and we are responsible for training and educating it.&lt;/p&gt;

&lt;p&gt;This generation won't be made up of developers who first learned to program and then incorporated AI into their work. That's us.&lt;/p&gt;

&lt;p&gt;They will learn to develop with it from day one.&lt;/p&gt;

&lt;p&gt;And those who don't adapt will be overtaken, much sooner than we think.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Was the Last Time You Looked at Assembly?
&lt;/h2&gt;

&lt;p&gt;Let me start with a question, and I hope the metaphor and the distance between the two things are clear.&lt;/p&gt;

&lt;p&gt;When was the last time you looked at the assembly generated by your compiler to check that it was managing registers, memory or loop efficiency properly?&lt;/p&gt;

&lt;p&gt;Probably never.&lt;/p&gt;

&lt;p&gt;And yet there was an entire generation of programmers who did. Who had to.&lt;/p&gt;

&lt;p&gt;Because early compilers generated worse code than a good assembly programmer could write. Trusting them blindly was simply reckless.&lt;/p&gt;

&lt;p&gt;Those programmers were right at that moment.&lt;/p&gt;

&lt;p&gt;The ones who were wrong were those who turned that temporary distrust into a permanent identity.&lt;/p&gt;

&lt;p&gt;Those who ten years later were still arguing that a good programmer had to write assembly while the rest of the world had moved up one level of abstraction.&lt;/p&gt;

&lt;p&gt;I think we're already inside that same kind of moment with generative AI.&lt;/p&gt;

&lt;p&gt;I'm not saying it's exactly the same.&lt;/p&gt;

&lt;p&gt;In fact, there are huge differences, as I said at the beginning.&lt;/p&gt;

&lt;p&gt;But conceptually, I see that we're facing another leap in abstraction.&lt;/p&gt;

&lt;p&gt;Until now we've been incorporating tools that helped us program. Editors. IDEs. Autocomplete. Frameworks. Stack Overflow. Copilot...&lt;/p&gt;

&lt;p&gt;All of them, in one way or another, allowed us to write code more easily or more quickly, with fewer errors or by failing earlier.&lt;/p&gt;

&lt;p&gt;But we were still writing the code.&lt;/p&gt;

&lt;p&gt;With agents, something different is starting to happen. More and more, we're moving away from saying “I want to write this code” and starting to say “I want this to happen”.&lt;/p&gt;

&lt;p&gt;This changes absolutely everything.&lt;/p&gt;

&lt;p&gt;Because the leap we're trying to make isn't simply from one tool to another, no. We're moving, with all the obvious differences, from assembly to the compiler, except this time the leap is several orders of magnitude bigger. An exponential leap.&lt;/p&gt;

&lt;p&gt;A compiler receives high-level code and generates low-level code. And yes, I know, following deterministic rules and within a bounded space. True. That's why I call it an exponential leap, because we're moving into an enormously more open space.&lt;/p&gt;

&lt;p&gt;But we're getting an AI to receive an idea, a need, a specification, some constraints or a conversation and produce software.&lt;/p&gt;

&lt;p&gt;And as I've already said, there's vastly more space between one thing and the other, and therefore vastly more things that can go wrong.&lt;/p&gt;

&lt;p&gt;And this is precisely one of the big differences compared with a compiler. A compiler is deterministic and verifiable. An agent isn't. It can make mistakes in subtle, plausible and difficult-to-detect ways.&lt;/p&gt;

&lt;p&gt;And the skeptic who says that the verification problem hasn't been solved is right. It hasn't.&lt;/p&gt;

&lt;p&gt;That's why I'm not saying that we can stop verifying what AI does. I'm saying that verifying it by reading every single line it generates can't be the solution either.&lt;/p&gt;

&lt;h2&gt;
  
  
  We Get Carried Away With AI
&lt;/h2&gt;

&lt;p&gt;At first the experience is spectacular. You ask it for a function. It does it.&lt;/p&gt;

&lt;p&gt;You ask it for an endpoint. Same thing.&lt;/p&gt;

&lt;p&gt;You explain a small feature and it changes four files, creates the tests and apparently everything works.&lt;/p&gt;

&lt;p&gt;And you think: “Holy shit.”&lt;/p&gt;

&lt;p&gt;Then you take another step. You give it something bigger. It starts touching things. Changes others. Interprets something differently from what you expected. Suddenly something doesn't add up. You tell it to fix it. It fixes that and breaks something else. You explain the new problem. It tries again. And shortly afterwards you have a mountain of useless, incomprehensible, unfixable and unmaintainable code.&lt;/p&gt;

&lt;p&gt;And you don't even really know when exactly it started fucking everything up.&lt;/p&gt;

&lt;p&gt;That's when a phrase we've heard quite a lot appears:&lt;/p&gt;

&lt;p&gt;“That's where you can tell whether you really know the fundamentals. If you know how things work, you can review what the AI has done and fix it.” Well, yes. Up to a point.&lt;/p&gt;

&lt;p&gt;Because let's be honest. There comes a point where not even Uncle Bob would understand a fucking thing about what it has done.&lt;/p&gt;

&lt;p&gt;And, besides, there's a much more important problem.&lt;/p&gt;

&lt;p&gt;If an AI can produce code much faster than I can read it, trying to control that AI by reading everything it generates simply doesn't scale.&lt;/p&gt;

&lt;p&gt;Some people talk about knowledge debt. But I don't think that's quite it. I think we're mixing two different things.&lt;/p&gt;

&lt;p&gt;It's like accepting the compiler but insisting on reviewing all the assembly it generates afterwards. It makes no sense. Especially with today's processors, whose assembly code very few humans are capable of understanding.&lt;/p&gt;

&lt;p&gt;Or, at least, it can't be the final solution.&lt;/p&gt;

&lt;p&gt;The fact that reviewing the code no longer scales doesn't mean the verification problem has been solved. It means exactly the opposite: we need another way to solve it.&lt;/p&gt;

&lt;p&gt;Even more so as agents keep getting better and we give them more and more autonomy.&lt;/p&gt;

&lt;p&gt;We need rules. A harness. Guardrails. External mechanisms that the agent doesn't control and that allow us to let it work without constantly looking over its shoulder.&lt;/p&gt;

&lt;p&gt;Not to tell it exactly how it has to do things. We don't need that anymore. We need to set the limits, prevent certain actions and check that what it delivers meets what we expect.&lt;/p&gt;

&lt;p&gt;But I'll talk about that in the second part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Having the Tool Doesn't Mean Getting Value From It
&lt;/h2&gt;

&lt;p&gt;And here another confusion appears that I see quite often.&lt;/p&gt;

&lt;p&gt;Having access to a tool is one thing and knowing how to get value from it is something very different.&lt;/p&gt;

&lt;p&gt;Today practically anyone can open ChatGPT, Claude, Copilot or any other agent and ask it to build something.&lt;/p&gt;

&lt;p&gt;That doesn't mean anyone can build a good product.&lt;/p&gt;

&lt;p&gt;In the same way that having an IDE doesn't make you a developer, having an AI capable of writing code doesn't automatically make you capable of creating software that delivers value.&lt;/p&gt;

&lt;p&gt;If writing code had really been the only barrier to creating good products, by now we should be seeing a massive explosion of disruptive applications.&lt;/p&gt;

&lt;p&gt;And I honestly don't see it.&lt;/p&gt;

&lt;p&gt;What I do see is a lot of vibe coders paying for advertising to try to sell, again and again, variations of the same worn-out product.&lt;/p&gt;

&lt;p&gt;Because code was never the product. The user doesn't want or need our code.&lt;/p&gt;

&lt;p&gt;They don't want our hexagonal architecture, our patterns, our framework or to know whether we use microservices or monoliths.&lt;/p&gt;

&lt;p&gt;They want to solve a problem.&lt;/p&gt;

&lt;p&gt;Code was, and still is, the means to achieve that. Or one of them.&lt;/p&gt;

&lt;p&gt;The difference is that this means has just become dramatically cheaper and more democratized.&lt;/p&gt;

&lt;p&gt;AI is making it extraordinarily cheap to &lt;strong&gt;plant software&lt;/strong&gt; (a brilliant analogy from the great eduferro).&lt;/p&gt;

&lt;p&gt;But planting isn't the same as having a good garden.&lt;/p&gt;

&lt;p&gt;Someone still has to decide what to plant, when to plant it and what for. Then it has to be watered, pruned, fertilized, whatever no longer serves a purpose has to be removed, and it has to be adapted when conditions change.&lt;/p&gt;

&lt;p&gt;Because software isn't something you build and finish. It's a living thing that evolves, and neglecting it is a great way to end up with a garden bed full of shit.&lt;/p&gt;

&lt;p&gt;Someone has to understand the problem. Know what's worth doing and what isn't. Choose between alternatives. Prioritize. Make decisions with incomplete information. Validate. Change their mind and iterate.&lt;/p&gt;

&lt;p&gt;And, above all, &lt;strong&gt;have judgment&lt;/strong&gt; to know whether what we've created actually delivers any value.&lt;/p&gt;

&lt;p&gt;The barrier to creating software is falling.&lt;/p&gt;

&lt;p&gt;The barrier to creating value, not necessarily.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our Work Is Shifting
&lt;/h2&gt;

&lt;p&gt;That's why I don't think the conclusion is that AI is going to replace software development.&lt;/p&gt;

&lt;p&gt;I think our work is shifting, because that work, as it exists today, will be replaced.&lt;/p&gt;

&lt;p&gt;One level up.&lt;/p&gt;

&lt;p&gt;If before we spent a large part of our effort telling a machine &lt;strong&gt;how&lt;/strong&gt; it had to do something through a programming language, increasingly we'll spend more effort defining &lt;strong&gt;what&lt;/strong&gt; should happen, why, under what conditions and how we can know whether the result is correct.&lt;/p&gt;

&lt;p&gt;That requires judgment.&lt;/p&gt;

&lt;p&gt;Probably much more than we needed to implement many of the tasks we do today.&lt;/p&gt;

&lt;p&gt;And this, for me, is where the truly important change lies.&lt;/p&gt;

&lt;p&gt;We're not stopping developing software.&lt;/p&gt;

&lt;p&gt;We're starting to stop being necessarily the ones who write every single line of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  And the Window Won't Stay Open Forever
&lt;/h2&gt;

&lt;p&gt;Every change in abstraction level opens a window of opportunity.&lt;/p&gt;

&lt;p&gt;It happened with version control, for example. With CI. With cloud. With infrastructure as code.&lt;/p&gt;

&lt;p&gt;For a while, those who adopted it well had a huge advantage over those who said: “We're fine like this.”&lt;/p&gt;

&lt;p&gt;Then it stopped being an advantage. It simply became the normal way of working.&lt;/p&gt;

&lt;p&gt;I think we're in one of those windows now.&lt;/p&gt;

&lt;p&gt;Except this time everything is happening much faster.&lt;/p&gt;

&lt;p&gt;Not adapting doesn't mean standing still. It means falling behind.&lt;/p&gt;

&lt;p&gt;Because while you keep working the same way, the bar for what we consider normal productivity keeps moving, but this time at a speed that makes it increasingly difficult to jump onto the train once it's already moving.&lt;/p&gt;

&lt;p&gt;Today, knowing how to work well with agents is a competitive advantage. Tomorrow it will simply be part of the job.&lt;/p&gt;

&lt;p&gt;But as I said before, first we have to solve an important problem, because if we're not going to review every single line produced by an AI...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;how the hell do we know whether what it has done is actually right?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's what the second part will be about.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>leadership</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Karajan v3.0.0: a local-first multi-agent orchestrator that conducts AI CLIs like an orchestra</title>
      <dc:creator>Mánu Fosela</dc:creator>
      <pubDate>Wed, 03 Jun 2026 22:46:30 +0000</pubDate>
      <link>https://dev.to/manufosela/karajan-v300-a-local-first-multi-agent-orchestrator-that-conducts-ai-clis-like-an-orchestra-59fi</link>
      <guid>https://dev.to/manufosela/karajan-v300-a-local-first-multi-agent-orchestrator-that-conducts-ai-clis-like-an-orchestra-59fi</guid>
      <description>&lt;p&gt;&lt;code&gt;karajan-code@3.0.0&lt;/code&gt; is on npm. &lt;code&gt;nvm install 22.22.1 &amp;amp;&amp;amp; npm i -g karajan-code@3&lt;/code&gt; and you're done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I built Karajan
&lt;/h2&gt;

&lt;p&gt;I love coding with AI. What I don't love is babysitting it.&lt;/p&gt;

&lt;p&gt;If you've spent any time with Claude Code, Codex, or Gemini, you know the loop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You describe what you want.&lt;/li&gt;
&lt;li&gt;You watch the model work.&lt;/li&gt;
&lt;li&gt;You catch it skipping tests, hallucinating an API, or touching the wrong layer, even with a solid &lt;code&gt;CLAUDE.md&lt;/code&gt;/&lt;code&gt;AGENTS.md&lt;/code&gt; and SKILLs in place.&lt;/li&gt;
&lt;li&gt;You stop it, correct it, relaunch it.&lt;/li&gt;
&lt;li&gt;Repeat.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Fine for a one-off change. Doesn't scale to a real project with a known plan, real tests, and a real quality gate. You end up being the human stuck in the middle of an async loop you can't pause, retry, or measure.&lt;/p&gt;

&lt;p&gt;I wanted something different. A tool where I describe what I want once and, from there, a set of AI agents (a coder, a reviewer, a tester, a SonarQube gate, a security auditor) run the loop themselves and ping me when it's done. Reproducibly. Locally. Using the subscriptions I already pay for.&lt;/p&gt;

&lt;p&gt;That tool is Karajan.&lt;/p&gt;

&lt;p&gt;The name is a nod to Herbert von Karajan, the conductor who never played a single instrument but imposed a unified vision on dozens of musicians until they sounded like one. Karajan didn't replace the musicians: he coordinated them under a single guiding intent. Karajan Code does the same with your AI agents: none of them is in charge, all of them converge under a single baton that arbitrates and unifies.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Karajan actually is
&lt;/h2&gt;

&lt;p&gt;Karajan is a local-first multi-agent orchestrator written in vanilla JavaScript on Node 22.&lt;/p&gt;

&lt;p&gt;You describe a task (in a markdown file, a user story, or directly on the command line) and Karajan runs a role-based pipeline over it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;task
  → triage      (classifies complexity, decides route)
  → researcher  (reads existing code)
  → architect   (designs the change)
  → planner     (breaks it into steps)
  → coder       (writes code and tests)
  → SonarQube   (static analysis as a gate)
  → tester      (runs the real test suite)
  → reviewer    (independent agent reviews the diff)
  → security    (security pass)
  → commiter    (commit, push, opens PR)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each role is a CLI agent. The coder can be Claude, the reviewer can be Codex, the tester can be Aider. Karajan doesn't call &lt;code&gt;/v1/messages&lt;/code&gt;: it spawns &lt;code&gt;claude -p&lt;/code&gt;, &lt;code&gt;codex&lt;/code&gt;, &lt;code&gt;gemini&lt;/code&gt;, &lt;code&gt;aider&lt;/code&gt;, &lt;code&gt;opencode&lt;/code&gt; as child processes and reads their stdout/stderr. That single architectural decision unlocks two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero API cost.&lt;/strong&gt; Your existing Pro / Plus / Max subscriptions do the work. No new bills.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-provider routing.&lt;/strong&gt; A powerful coder + a strict reviewer + a cheap triage agent = the right model for each role, with automatic fallback when one hits its quota.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The whole system is TDD-first by default. The coder writes tests alongside the code, the tester runs them for real, the reviewer sees the diff. If something fails, the reviewer feeds back to the coder and the loop iterates, bounded by &lt;code&gt;maxIterations&lt;/code&gt; and &lt;code&gt;maxIterationMinutes&lt;/code&gt; so it can't spiral.&lt;/p&gt;

&lt;p&gt;A few more pieces worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MCP server.&lt;/strong&gt; Karajan ships as an MCP server, so you can drive the full pipeline from inside Claude Code, Cursor, Codex, or any MCP host. &lt;code&gt;kj_run&lt;/code&gt;, &lt;code&gt;kj_plan&lt;/code&gt;, &lt;code&gt;kj_code&lt;/code&gt;, &lt;code&gt;kj_review&lt;/code&gt;, &lt;code&gt;kj_audit&lt;/code&gt;, &lt;code&gt;kj_rag_query&lt;/code&gt;: full pipeline access without leaving the conversation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solomon, the AI judge.&lt;/strong&gt; An independent role the Brain orchestrator consults when there's a real dilemma (security vs. deadline, two reviewers disagreeing). Not on every loop; only when the Brain can't decide deterministically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Story Board.&lt;/strong&gt; A small local web dashboard at &lt;code&gt;http://localhost:4000&lt;/code&gt; showing every user story, every session, every plan, every RAG metric. The single source of truth for the team.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local RAG.&lt;/strong&gt; Each project gets its own embedded code index (&lt;code&gt;~/.karajan/rag.db&lt;/code&gt;). The pipeline retrieves real code before planning, so the architect sees what already exists. Six embedding providers (Ollama, OpenAI, Voyage, Cohere, Mistral, local ONNX). Tree-sitter-based chunkers for JS, TS, Python, Rust, Go, and Java.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;kj audit&lt;/code&gt;.&lt;/strong&gt; A deterministic-first audit command: dead code, unused dependencies, complexity growth, security findings (OSV, Semgrep, SonarQube), accessibility hints, Web Performance budgets, and an AI Harness Scorecard run via Docker that gives an objective 0-100 score for "how AI-friendly is this repo right now." Keeps a per-project history and draws sparkline trends.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What v3.0.0 brings
&lt;/h2&gt;

&lt;p&gt;v3.0.0 is a runtime alignment, not a feature drop.&lt;/p&gt;

&lt;p&gt;Node 20 hit EOL on 2026-04-30. The Active LTS line is Node 22. Three of Karajan's dependencies independently bumped to majors requiring Node 22+: &lt;code&gt;lint-staged&lt;/code&gt; 17, &lt;code&gt;commander&lt;/code&gt; 15, and &lt;code&gt;better-sqlite3&lt;/code&gt; 12.10. Rather than publishing four staggered minors patching each constraint separately, v3.0.0 bundles the runtime bump with the dep majors that depend on it.&lt;/p&gt;

&lt;p&gt;Migration is a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nvm &lt;span class="nb"&gt;install &lt;/span&gt;22.22.1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; nvm use 22.22.1
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; karajan-code@3
kj doctor    &lt;span class="c"&gt;# checks runtime + HW + tooling&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you were already on Node 22+ and your &lt;code&gt;~/.karajan/&lt;/code&gt; works, nothing changes. No public API changes: &lt;code&gt;kj run&lt;/code&gt;, &lt;code&gt;kj plan&lt;/code&gt;, MCP tools, role templates, RAG, Story Board, audit, telemetry, all intact. Existing sessions, plans, RAG index, and audit history are forward-compatible; nothing to migrate by hand.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why a major with no API changes?
&lt;/h3&gt;

&lt;p&gt;Semver. Bumping the minimum Node version is a breaking change for downstream consumers: an install that worked on Node 20 now hard-fails. That alone qualifies, and the three dep majors on board each qualify on their own semver. Cutting a single v3.0.0 surfaces the runtime change once instead of four times.&lt;/p&gt;

&lt;p&gt;The release also brings documentation polish: a new footprint and hardware requirements section in the README so you know what you're getting into before installing. &lt;code&gt;kj&lt;/code&gt; itself is 5.2 MB on npm and &lt;code&gt;~/.karajan/&lt;/code&gt; runs around 40 MB. Ollama (optional, 6.55 GB), SonarQube (optional, 1.47 GB), and the qmd cache (optional, ~2.2 GB) are the heavy optional layers. Three profiles: Minimal ~250 MB, Recommended ~8.5 GB, Full ~11 GB.&lt;/p&gt;

&lt;h2&gt;
  
  
  v1 to v3: how Karajan grew
&lt;/h2&gt;

&lt;p&gt;Karajan didn't start as a multi-agent orchestrator. It started as a shell script.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;v0.x: One script.&lt;/strong&gt; task → claude → diff → codex review → done. Hardcoded to two agents. No retry, no cost tracking, no SonarQube, no test integration. It worked, roughly, on a Tuesday afternoon.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v1.0 to v1.1: Quality gates and roles.&lt;/strong&gt; SonarQube became a mandatory step between coder and reviewer. The TDD policy meant any code change required a test change. Then the monolithic script became a role-based pipeline: &lt;code&gt;BaseRole&lt;/code&gt;, &lt;code&gt;BaseAgent&lt;/code&gt;, 12 configurable roles, review profiles, Solomon escalation, budget tracking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v1.2 to v1.3: MCP server and extensibility.&lt;/strong&gt; The MCP server made Karajan callable from inside any MCP-speaking AI agent. The plugin system let users wrap their own CLIs as Karajan agents without forking. Planning Game integration connected the pipeline to project management. Git automation closed the loop with auto-commit, auto-PR, auto-rebase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v1.4 to v1.7: Resilience.&lt;/strong&gt; Rate-limit detection across all supported CLIs. Automatic fallback when the primary coder hits its quota. Smart model selection mapping triage complexity to model tier (trivial → Haiku, complex → Opus). Interactive checkpoints replacing the brittle hard timeout. In-process MCP execution so subprocess SIGKILLs stopped killing agents mid-work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v1.40 to v1.46: Sovereignty, OpenSkills, parallel stories.&lt;/strong&gt; The pipeline learned to defend its own decisions against host AI overrides (pipeline sovereignty guard). OpenSkills brought domain knowledge to coders, reviewers, and architects. Parallel user stories ran independent stories concurrently in git worktrees. First SEA binaries appeared: &lt;code&gt;kj&lt;/code&gt; running without Node installed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v1.50 to v1.58: Telemetry, domain knowledge, i18n, WebPerf.&lt;/strong&gt; Telemetry (opt-out, no code or personal data) to finally measure what was happening. Domain knowledge files (&lt;code&gt;DOMAIN.md&lt;/code&gt;) injecting business context into every role. i18n letting the pipeline speak EN or ES end to end. Web Performance as a first-class quality gate (Core Web Vitals via Chrome DevTools MCP, inspired by Joan Leon's WebPerf Snippets).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v2.0: Karajan Brain.&lt;/strong&gt; A central AI orchestrator deciding routing, enriching feedback, suggesting direct actions, and compressing outputs between roles. Solomon moved from "every loop" to "real dilemmas only." Tester and security became blocking gates. The proxy subsystem (which never worked well under SSE/WebSockets) was dropped in favor of RTK for token savings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v2.x: The "I use this daily" phase.&lt;/strong&gt; Too many releases to list, but the highlights: stack-aware audit (v2.9.0), Docker/shell installer (v2.10.0), Story Board hardening (v2.13.0), plan adherence scoring + golden tasks (v2.12.0), shared team Story Board (v2.31.0), editable config UI (v2.30.0), multi-language RAG with AST chunkers for Python/Rust/Go/Java (v2.34.0), retrieval quality harness (&lt;code&gt;kj rag eval&lt;/code&gt;, v2.34.0), and the AI Harness Scorecard golden metric integrated into &lt;code&gt;kj audit&lt;/code&gt; (v2.32.0-v2.33.0).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v3.0.0: Runtime alignment.&lt;/strong&gt; No new features. Node 22 baseline, three dep majors, one migration command, zero public API change. The story of "boring releases that buy you a year of headroom."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That arc, compressed: a shell script → a role-based pipeline → an orchestrator with a brain → a local-first, MCP-driven, RAG-powered, multilingual, multi-provider, test-first team tool. The same idea throughout: many different AI agents, one baton that makes them converge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nvm &lt;span class="nb"&gt;install &lt;/span&gt;22.22.1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; nvm use 22.22.1
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; karajan-code@3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Smoke test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kj &lt;span class="nt"&gt;--version&lt;/span&gt;           &lt;span class="c"&gt;# 3.0.0&lt;/span&gt;
kj doctor              &lt;span class="c"&gt;# checks Node, agents, Docker, SonarQube, RAG...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initialize a project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;your-repo
kj init                &lt;span class="c"&gt;# autodetects stack, suggests agents, scaffolds .karajan/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run a task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kj run &lt;span class="s2"&gt;"Add a /healthz endpoint with a unit test that returns 200"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--coder&lt;/span&gt; claude &lt;span class="nt"&gt;--reviewer&lt;/span&gt; codex
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or from inside Claude Code / Cursor / any MCP host, just call &lt;code&gt;kj_run&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Open the Story Board:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kj board start         &lt;span class="c"&gt;# http://localhost:4000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the loop. Describe what you want. Let the orchestra play.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to find it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;npm:&lt;/strong&gt; &lt;code&gt;karajan-code&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docs:&lt;/strong&gt; &lt;a href="https://karajancode.com/docs" rel="noopener noreferrer"&gt;https://karajancode.com/docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repo:&lt;/strong&gt; github.com/manufosela/karajan-code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License:&lt;/strong&gt; AGPL-3.0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Author:&lt;/strong&gt; &lt;a href="https://github.com/manufosela" rel="noopener noreferrer"&gt;@manufosela&lt;/a&gt;, built in the open.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try it and something surprises you (good or bad), I want to know. Open an issue, reach out on GitHub, or reply here.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>javascript</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I’ve tried RTK and it saves a lot of tokens, but I think Squeezr is on another level.</title>
      <dc:creator>Mánu Fosela</dc:creator>
      <pubDate>Sun, 05 Apr 2026 16:37:21 +0000</pubDate>
      <link>https://dev.to/manufosela/ive-tried-rtk-and-it-saves-a-lot-of-tokens-but-i-think-squeezr-is-on-another-level-2fjo</link>
      <guid>https://dev.to/manufosela/ive-tried-rtk-and-it-saves-a-lot-of-tokens-but-i-think-squeezr-is-on-another-level-2fjo</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/sergioramosv/i-was-burning-through-ai-tokens-without-realizing-it-heres-how-i-fixed-it-bn" class="crayons-story__hidden-navigation-link"&gt;I was burning through AI tokens without realizing it. Here's how I fixed it.&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/sergioramosv" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F3862441%2F4095b39e-a525-4be1-bff2-26abcb1fa47a.jpg" alt="sergioramosv profile" class="crayons-avatar__image" width="96" height="96"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/sergioramosv" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Sergio Ramos Vicente
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Sergio Ramos Vicente
                
              
              &lt;div id="story-author-preview-content-3457029" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/sergioramosv" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F3862441%2F4095b39e-a525-4be1-bff2-26abcb1fa47a.jpg" class="crayons-avatar__image" alt="" width="96" height="96"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Sergio Ramos Vicente&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/sergioramosv/i-was-burning-through-ai-tokens-without-realizing-it-heres-how-i-fixed-it-bn" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Apr 5&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/sergioramosv/i-was-burning-through-ai-tokens-without-realizing-it-heres-how-i-fixed-it-bn" id="article-link-3457029"&gt;
          I was burning through AI tokens without realizing it. Here's how I fixed it.
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tooling"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tooling&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/claude"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;claude&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/sergioramosv/i-was-burning-through-ai-tokens-without-realizing-it-heres-how-i-fixed-it-bn" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;3&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/sergioramosv/i-was-burning-through-ai-tokens-without-realizing-it-heres-how-i-fixed-it-bn#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Over-Architecting in Web Design</title>
      <dc:creator>Mánu Fosela</dc:creator>
      <pubDate>Thu, 10 Oct 2024 05:01:23 +0000</pubDate>
      <link>https://dev.to/manufosela/on-architecture-in-web-design-367n</link>
      <guid>https://dev.to/manufosela/on-architecture-in-web-design-367n</guid>
      <description>&lt;p&gt;I recently read &lt;a href="https://x.com/justinfagnani/status/1844080475612270887" rel="noopener noreferrer"&gt;a post&lt;/a&gt; by &lt;a class="mentioned-user" href="https://dev.to/justinfagnani"&gt;@justinfagnani&lt;/a&gt; on X that says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Lit is not a framework. The browser is the framework."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This got me thinking about how we've been building for the web in recent years.&lt;/p&gt;

&lt;p&gt;For quite some time now, if you know HTML, CSS, and JavaScript, you really don’t need a framework to build web applications. At most, you might use some libraries to simplify certain tasks, but even these are gradually being incorporated into the platform itself, like routing or state management.&lt;/p&gt;

&lt;p&gt;Our focus, however, has shifted towards learning frameworks, and now meta-frameworks, which do make it easier to build web environments. They optimize pages, automatically generate necessary files like &lt;code&gt;sitemaps.xml&lt;/code&gt;, optimize images, remove unused CSS, bundle, minify, and optimize JavaScript. They’re effective, yes. But they don't adhere to the standard.&lt;/p&gt;

&lt;p&gt;Meta-frameworks like Astro let you use standards, but they don’t exactly make it easy. Instead, they streamline using other frameworks or their own components in a more user-friendly, non-standard format to "simplify" component creation. They also employ techniques to facilitate SEO, loading, and indexing—depending on how you’ve implemented them.&lt;/p&gt;

&lt;p&gt;To me, this approach has two major issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cognitive overload&lt;/strong&gt;: Developers must learn to manage not only the framework itself but now also the meta-framework and its set of tools, which often adds unnecessary complexity to project architecture.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Detachment from the standard&lt;/strong&gt;: Focusing on frameworks means we’re not investing time in understanding the latest developments in the web itself, which are substantial. CSS, for example, is making a huge leap forward with features like &lt;code&gt;container queries&lt;/code&gt;. Meanwhile, JavaScript and HTML are also evolving, with HTML potentially introducing a new &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; element that will allow HTML and CSS within &lt;code&gt;&amp;lt;option&amp;gt;&lt;/code&gt;, opening new and more flexible ways to create rich selectors.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In many frameworks, though, these innovations are implemented through their own components, so to developers, it can seem like these new features are framework inventions instead of standard advancements.&lt;/p&gt;

&lt;p&gt;I remember a few years ago, when these meta-frameworks were just emerging, a developer excitedly told me, “They’ve invented SSR (Server-Side Rendering); now we can generate HTML on the server, creating static pages.” I looked at him in surprise. He insisted, thinking I was just as impressed with this “novelty” saying, “Isn’t it incredible? What a fantastic idea.” The irony was that SSR has been around since the early days of the web.&lt;/p&gt;

&lt;p&gt;Today’s web developers, those studying web development and entering the field, often lack historical context. They don’t know how web development originally worked. They don’t understand the reasons we moved from server-side rendering to client-side rendering and started using frameworks. Somewhere along the line, when “Ajax” gained popularity, we lost our way—probably driven by the desire for standards to cover everything we needed, while big companies fought to impose their own criteria, tags, and languages in the browser (I still remember Microsoft’s infamous VisualBasicScript).&lt;/p&gt;

&lt;p&gt;Frameworks aren’t the problem. Not at all. They added power and value to web development. They allowed us to create applications for everyone, spurring the rise of hundreds of thousands of web developers and bootcamps.&lt;/p&gt;

&lt;p&gt;Frameworks and meta-frameworks aren’t the issue; they even helped democratize web development, enabling millions to create applications and fueling the bootcamp boom. But as the web platform matures and standards become more robust, framework creators have business incentives to retain and promote their tools. They’re not going to say, “Stop using my framework; you can do everything with standards now.” Plus, migrating applications built with these frameworks to the standard is nearly impossible, especially since, in my experience, most lack E2E or functional tests to ensure that migration maintains the original behavior.&lt;/p&gt;

&lt;p&gt;So, why aren’t we investing in tools that help us leverage today’s standards?&lt;/p&gt;

&lt;p&gt;There isn’t a meta-framework that “forces” you to use the standard, making it easier in the process.&lt;/p&gt;

&lt;p&gt;We need solutions that help us organize our projects efficiently and assist with build and optimization tasks if we want to do SSR, CSR, SSG, ISR, Hydration, create MPAs or SPAs. But we should be fully aware of our needs and the pros and cons of each choice. These tools should optimize images, JavaScript, CSS, and generate only the necessary files. They should allow the use of libraries that stick to the standard and adequately cover the platform’s remaining gaps. They should optimize and support SEO, loading, and indexing, facilitate testing, and harness the platform’s power.&lt;/p&gt;

&lt;p&gt;It’s time to reduce over-engineering and excessive architectural design in our web projects. Let’s keep learning, not just how to use a framework, but also how to appreciate and apply the incredible advancements in web standards.&lt;/p&gt;

&lt;p&gt;No excuses.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>inspiration</category>
    </item>
    <item>
      <title>Desarrollo Web y Web Components</title>
      <dc:creator>Mánu Fosela</dc:creator>
      <pubDate>Tue, 29 Dec 2020 01:04:19 +0000</pubDate>
      <link>https://dev.to/manufosela/desarrollo-web-y-web-components-1j0g</link>
      <guid>https://dev.to/manufosela/desarrollo-web-y-web-components-1j0g</guid>
      <description>&lt;h1&gt;
  
  
  Disclaimer
&lt;/h1&gt;

&lt;p&gt;Lo que vierto en este post es mi opinión. Mi punto de vista. Mi percepción. Desde mi experiencia y mis sesgos. Y sobre todo desde mi ignorancia en muchos campos.&lt;/p&gt;

&lt;h1&gt;
  
  
  Sobre mi y mis inicios como desarrollador
&lt;/h1&gt;

&lt;p&gt;Llevo programando para la web desde 1997.&lt;br&gt;
A parte de HTML, CSS y Javascript, he pasado por varios lenguajes: CGIs en C, ASP, JSP, PHP, Java, Perl y Ruby si no recuerdo mal. Algunos durante unos meses otros durante años.&lt;br&gt;
He trabajado con bases de datos MSSQL, MySQL, Oracle, IBMDb2 y MongoDB.&lt;br&gt;
Y he trabajado con servidores IIS, Apache, Tomcat y Nginx.&lt;br&gt;
Desde siempre Windows (era un mal necesario) y Linux (era un suplicio a principios de siglo)&lt;/p&gt;

&lt;p&gt;He visto cómo hemos pasado de que un desarrollador/equipo web fuera responsable de todo a la especialización más exquisita.&lt;br&gt;
Aprendí a hacer testing, TDD, metodología XP y pair programing en 2007. Hacia 2008 con el boom de Javascript y Jquery, empecé a especializarme más en Javascript, aunque no fue hasta 2010 cuando pasé a ser “oficialmente” FrontEnd Developer, a pesar de que mis dotes de diseño dejan mucho que desear.&lt;br&gt;
Y dejé de tocar la parte back y configuración de servidores. Dejé de subir a producción por FTP y empecé a usar control de versiones con CVS y Subversion.&lt;/p&gt;

&lt;p&gt;Luego empezaron a aparecer librerías como Backbone y arrancó el boom de los frameworks del front, las llamadas AJAX y el desarrollo de APIs que alimentaban esos frontales “mágicos”&lt;/p&gt;

&lt;p&gt;Por cierto, AJAX empecé a usarlo hacia 2007 cuando existía ¡¡desde 2002!! Y yo usando iframes ocultos, leyendo información y cargándola en la página principal, una “guarrada”... pero es que antes era otra epoca ;)&lt;/p&gt;

&lt;p&gt;En 2015 me enganché a los web-components, con Polymer en su versión 0.8 y a la espera de que los ansiados estándares vieran su primera versión que no llegó hasta 3 años después.&lt;br&gt;
Y aunque siempre he probado, y hecho alguna aplicación que otra, en todos los frameworks típicos, nunca me han entusiasmado en exceso.&lt;br&gt;
Sí. Mola. Facilitan. Ahorran tiempo (una vez les coges el punto). Unifican los grandes desarrollos. La curva de aprendizaje puede venir hecha al incorporarte a un equipo. Y sobre todo hacen mucha “magia” que se puede traducir en desarrollo mas rápido y menos dolores de cabeza, pero ¿necesitaba usarlos?&lt;/p&gt;

&lt;h1&gt;
  
  
  Empiezo la polémica
&lt;/h1&gt;

&lt;p&gt;Creo que en los proyectos grandes donde he trabajado, o he visto trabajar desde que ejerzo como manager, está plenamente justificado el uso de un framework, pero ¿Y en un blog? ¿Y en una web de contenidos? ¿Y en algunos juegos? En internet veo muchas webs que son contenidos y creo que no necesitan un framework para desarrollarlas.&lt;/p&gt;

&lt;p&gt;¿Que se pueden usar frameworks? Correcto. ¿Que no penaliza? Bueno, depende. A nivel de lo que es una página web, solo hace falta hacer “botón derecho” (lo siento no uso Mac), “ver código fuente de la página” y ver en dicho código fuente “atrocidades” que ni DreamWeaver en sus mejores momentos…&lt;br&gt;
Porque o bien no hay contenido, ya que se genera dinámicamente, y por tanto es contenido invisible a los crawlers de los buscadores, o bien lo que se escupe aparece “ofuscado”, cuando una web de contenido, justo lo que se tiene que leer perfectamente es el contenido, con sus tags HTML, usando la semántica de las etiquetas HTML (cuantos años luché al principio por una web Semántica)&lt;br&gt;
O eso creí entender allá por 1997 cuando leí mi primer libro de HTML.&lt;/p&gt;

&lt;p&gt;Y lo de que no es necesario usar un framework para generar contenido como un blog parece que se están dando cuenta la comunidad.&lt;br&gt;
Por eso ¿Qué está pasando últimamente? Que alguien “descubre” el SSR (Server Side Rendering)&lt;br&gt;
Y empiezan a aparecer frameworks sobre los frameworks para hacer lo que se hacía desde siempre, generar el contenido de la página desde en el servidor y que el navegador haga la petición y le envíe dicho contenido, eso sí, ahora usando toooooda la lógica del framework sobre otro framework. Volvemos a los inicios, pero complicándolo. &lt;br&gt;
¿Por qué? Quizás porque lo no sabemos hacer de otra manera. Es lo que nos han enseñado.&lt;/p&gt;

&lt;p&gt;Nos enseñan a usar algunas herramientas, pero &lt;strong&gt;no nos enseñan a pensar cuándo se deben usar dichas herramientas&lt;/strong&gt;.&lt;br&gt;
Como se suele decir: “Te enseñan a usar un martillo y todo son clavos”&lt;br&gt;
¿Te enseñan el framework AcojoJS? Todo son aplicaciones con AcojoJS. Sin pararnos a pensar si es la solución idónea para nuestro problema. Funciona, ¿no?&lt;/p&gt;

&lt;p&gt;Quizás porque no nos enseñan más.&lt;br&gt;
Quizás porque no nos ponemos a pensar.&lt;br&gt;
Quizás porque va todo tan deprisa que nos falta tiempo para aprender.&lt;/p&gt;

&lt;p&gt;Porque aprendemos a usar un martillo, es decir a programar, y ¡ale! a clavar todo lo que pille, aunque sea un tornillo o un cristal.&lt;/p&gt;

&lt;p&gt;Y es cierto. Aprendes lo suficiente para usar ese martillo con suficiente destreza. Pero nada más.&lt;br&gt;
Aunque seamos conscientes de que la caja de herramientas es gigante y la cantidad de herramientas casi infinita, nos quedamos con nuestro martillo.&lt;/p&gt;

&lt;p&gt;Pero sobre todo, creo que no se fomenta el buscar soluciones adecuadas a los problemas, si no a usar nuestra solución a cualquier problema.&lt;br&gt;
Es decir, &lt;strong&gt;adaptamos problemas a nuestras soluciones&lt;/strong&gt;, complicando más el problema.&lt;/p&gt;

&lt;p&gt;Esto me pasó en uno de mis equipos, cuando nos plantearon un proyecto en el que teníamos vía libre para utilizar lo que quisiéramos. Greenfield Project lo llaman.&lt;br&gt;
Pregunté:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;¿Qué usamos? 
Todos al unísono: &lt;/li&gt;
&lt;li&gt;Vue! Vue! Vue!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mi pregunta era para implicar al equipo en el análisis del problema y buscar la solución adecuada, que facilitara el trabajo y la entrega a tiempo.&lt;br&gt;
Pero mi sorpresa fue mayúscula cuando con un mínimo de información sobre el proyecto, todos pedían a Barrabás, en este caso Vuerrabás :P&lt;br&gt;
Menos mal que en este caso no hice como Poncio Pilato, aunque si me lleve un Soponcio Parrato :P&lt;/p&gt;

&lt;p&gt;Este es solo un claro ejemplo de a lo que nos conduce el mundo del desarrollo actual, del que me siento parte y responsable.&lt;/p&gt;

&lt;h1&gt;
  
  
  WebComponents
&lt;/h1&gt;

&lt;p&gt;Y ya metidos en faena, quiero también reivindicar a los denostados, en algunos círculos, web-components estándar, incluidos en muchos frameworks, pero sin pertenecer a ninguno.&lt;/p&gt;

&lt;p&gt;Y sobre todo quiero plantear mi disconformidad con el uso que se hace de ellos con el “Atomic Design”, es decir, crear componentes atómicos, que pueden usarse solos o como parte de un componente de orden superior, conocidos como “moléculas”. Y que estos últimos a la vez puedan conformar “organismos” de componentes…&lt;/p&gt;

&lt;p&gt;No se. Me chirría. Siempre he visto los web-components como etiquetas HTML, como un &amp;lt;UL&amp;gt; o un &amp;lt;TABLE&amp;gt; o &amp;lt;DIV&amp;gt; solo que “haga más cosas”. Igual que crearon el tag &amp;lt;VIDEO&amp;gt;, que es realmente el concepto de un web-component aplicado: permite insertar vídeo en la web de manera sencilla, con su controlador del vídeo, la pantalla de vídeo, admitiendo diferentes formatos y fuentes…&lt;/p&gt;

&lt;p&gt;Y veo bien web-components que aporten “cosas” que no hay. Un lector de QR. Un campo de formulario para votar con estrellas. Un reloj digital. Un calendario. Un menú. Hay mil ejemplos de necesidades.&lt;/p&gt;

&lt;p&gt;¿Pero es un componente un ente formado por un &amp;lt;h1&amp;gt;, un &amp;lt;h2&amp;gt; y un &amp;lt;IMG&amp;gt; donde además ninguno de los 3 elementos tiene que aparecer siempre, pudiendo incluso llegar a ser solamente uno de ellos…?&lt;/p&gt;

&lt;p&gt;El argumento suele ser “es que se repite”, “es que así es flexible”, “es que si no ¿cómo lo hacemos?”&lt;/p&gt;

&lt;p&gt;Pues puede ser un componente web, cierto. Pero también puede no serlo. Si usas un framework quizás no te quede más remedio. O si no te han enseñado a cómo hacerlo sin que lo sea.&lt;/p&gt;

&lt;p&gt;Bajo mi punto de vista, si usas web-components y estás generando páginas estáticas, aunque se alimente el contenido de una base de datos, al fin y al cabo, ese ente de &amp;lt;h1&amp;gt;&amp;lt;h2&amp;gt;&amp;lt;img&amp;gt; es simplemente una plantilla HTML.&lt;/p&gt;

&lt;p&gt;Y puedo tener plantillas con combinaciones de esos 3 elementos, si realmente se llaman a todas las combinaciones de los mismos, porque si no, emplear tiempo en tenerlo “por si acaso” es tontería. Ese es otro de los grandes problemas del mundo del desarrollo que da para otro post.&lt;/p&gt;

&lt;p&gt;Volviendo al problema planteado. Creo que siempre y cuando sean una simple presentación de tags HTML agrupados, sin ninguna funcionalidad, es una simple plantilla.&lt;br&gt;
Si tuviera funcionalidad, entonces sí podría tener sentido que fuera un web-component. O si tuviera un diseño especial, que pueda chocar con el diseño global y necesite encapsular dicho diseño, también podría ser un web-component.&lt;/p&gt;

&lt;p&gt;Es decir, creo que un web-component, la razón de crearlo es cuando:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Se puede reutilizar.&lt;/li&gt;
&lt;li&gt;Tiene una presentación que no existe ya en un tag HTML&lt;/li&gt;
&lt;li&gt;Tiene una funcionalidad que le da entidad y sentido.&lt;/li&gt;
&lt;li&gt;Tiene una parte de su presentación/diseño abstraído del entorno donde se use.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Si no cumple alguno de esos puntos, debo analizar si realmente necesito diseñar un web-component o tengo alguna alternativa.&lt;/p&gt;

&lt;p&gt;Y siguiendo esta premisa, por lo tanto para mi no tiene sentido un web-component no visual, que solo aporte funcionalidad, como a veces he visto los llamados “data managers”.&lt;/p&gt;

&lt;p&gt;Para mi eso es un tag HTML que ya existe. Se llama &amp;lt;script&amp;gt;. Creo una “librería” con la funcionalidad que necesito. El API que he convenido. Y todo el que lo necesite lo usa. No veo la necesidad de hacerlo mediante un web-component.&lt;/p&gt;

&lt;h1&gt;
  
  
  Comunicación entre web-components.
&lt;/h1&gt;

&lt;p&gt;Otra "unpopular opinion". Siempre se dice, yo de hecho lo he preguntado en alguna entrevista, ¿Cómo se comunican los componentes? Y quien se lo sabe, repite el mantra: “De padres a hijos mediante propiedades, de hijos a padres mediante eventos”&lt;/p&gt;

&lt;p&gt;Bueno, desde el “Atomic Design” quizás ese mantra sí tiene sentido.&lt;br&gt;
Pero si concibes los web-components cómo “tags” autónomos, que pueden convivir y estar anidados dentro de otros, pero independientes, la comunicación debería ser siempre mediante eventos. O mediante prospección.&lt;/p&gt;

&lt;p&gt;O sea, aunque puede haber dependencias entre web-components, como la hay en un &amp;lt;table&amp;gt; con un &amp;lt;tr&amp;gt; y este con un &amp;lt;td&amp;gt; o un &amp;lt;ul&amp;gt; con un &amp;lt;li&amp;gt;. Pero realmente puedes usarlos por separado, aunque su funcionamiento no sea el esperado o no sea correcto.&lt;/p&gt;

&lt;p&gt;Soy más partidario de un &lt;strong&gt;“Phenotypic Design”&lt;/strong&gt; para los web-components. Es decir, diseñar los web-components basándonos en el conjunto de características visibles como resultado de la interacción entre su tipo y el medio.&lt;/p&gt;

&lt;p&gt;Los tags HTML están clasificados por su tipo: Fundación, Formato, Formulario, Marco, Imagen, Audio, Enlace, Lista, Tabla, Estilo, Meta información y Programa.&lt;br&gt;
¿Por qué no seguir esta clasificación y ampliarla para los web-components si es necesario? &lt;/p&gt;

&lt;p&gt;Usando el concepto de “parientes” he visto siempre que ineludiblemente terminamos acoplando componentes padres, madres, hijos, sobrinas, abuelas y tíos todos entre sí, como buena familia. Pero ese no es el fin de los web-components, ya que solemos perder re-utilización fuera de la “familia”, creando verdaderas “mafias”.&lt;/p&gt;

&lt;p&gt;Un web-component, por estar anidado dentro de otro no tiene porque crear una relación padre-hijo. Para mi tienen que crear una relación simbiótica. Y las hay de muchos tipos. Pueden ser un alga-hongo, creando un web-component de más valor. Árbol-ardilla, donde uno usa al otro para darle “cobijo”. &lt;br&gt;
Creo que aporta una visión más amplia, menos limitante, donde es más fácil de mantener la individualidad, y por tanto la re-utilización de web-components en diferentes entornos, aplicaciones o webs.&lt;/p&gt;

&lt;h1&gt;
  
  
  Indexabilidad de los web-components
&lt;/h1&gt;

&lt;p&gt;Otro tema que me ha preocupado siempre es que la encapsulación que hace el shadow DOM en los web-components que hace que su contenido no sea indexable y por tanto desaparece al darle a “ver código fuente de la página”, siendo invisible para los crawlers.&lt;/p&gt;

&lt;p&gt;Pero esto creo que también es por un mal uso que le estamos dando a los web-components.&lt;/p&gt;

&lt;p&gt;Si tengo por ejemplo un web-component de un menú de navegación, que al final puede ser un tag &amp;lt;nav&amp;gt; al que le hemos dado una funcionalidad especial en base a unos requisitos, incluso diferente en diferentes resoluciones pantalla, y al que le hemos dado un diseño concreto.&lt;br&gt;
¿Por qué no utilizar el light-dom, es decir, el dom dentro del web-component, leer este en el ciclo de creación del mismo, y pintarlo conforme a los requisitos que necesitamos?&lt;br&gt;
Estaríamos dando el contenido al crawler y encapsulando maquetación, diseño y funcionalidad. ¡Voila!&lt;/p&gt;

&lt;p&gt;Porque otra aberración que veo bastante por ahí es pasar información a un web-component mediante un json ¡¡¡que insertamos como atributo!!! &lt;br&gt;
¿Por qué no generar esa información en el light-dom con HTML semántico, indexable y accesible y luego “darle forma” en el web-component?&lt;/p&gt;

&lt;p&gt;Es cierto que puede penalizar algo el tiempo de renderizado, pero no más si lo que tienes que leer es un json o una lista de elementos como atributo para pintarlos.&lt;br&gt;
Y ademaś existen técnicas para poner un “loader” o añadir una animación de fade-in después del primer renderizado.&lt;/p&gt;

&lt;p&gt;Por otro lado, si metemos el contenido del web-component en el light-dom, en el remoto caso de que no funcionara javascript, incluso podríamos asegurarnos de que la página tuviera contenido ya que el light-dom se renderiza, aunque probablemente se verá como “HTML feo” al no cargar el CSS de los componentes y no tener CSS para esos elementos HTML del light-dom.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/manufosela/pen/MWjrrEW" rel="noopener noreferrer"&gt;Ejemplo de componente nav-list&lt;/a&gt; &lt;br&gt;
En el repo de git se puede ver la evolución según versiones.&lt;/p&gt;

&lt;h1&gt;
  
  
  Mis conclusiones
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;No nos enseñan a buscar soluciones a los problemas, sino a usar las soluciones que conocemos ante cualquier problema.&lt;/li&gt;
&lt;li&gt;Crear un web-component cuando:

&lt;ul&gt;
&lt;li&gt;Se puede reutilizar.&lt;/li&gt;
&lt;li&gt;Tenga una presentación que no existe ya en un tag HTML&lt;/li&gt;
&lt;li&gt;Tenga una funcionalidad que le da entidad y sentido.&lt;/li&gt;
&lt;li&gt;Tenga necesidad de abstraer presentación/diseño del entorno donde se use.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Relación entre componentes simbiótica frente a la “familiar”&lt;/li&gt;

&lt;li&gt;Comunicación entre componentes mediante eventos.&lt;/li&gt;

&lt;li&gt;“Phenotypic Design” frente a “Atomic Design”&lt;/li&gt;

&lt;li&gt;Utilizar el light-dom cuando sea posible, para insertar contenido al web-component y hacerlo indexable&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Y si has leído hasta aquí ¡gracias! :)&lt;br&gt;
Nos vemos por la comunidad.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webcomponents</category>
      <category>javascript</category>
      <category>html</category>
    </item>
    <item>
      <title>Javascript Proxy: algunos usos prácticos</title>
      <dc:creator>Mánu Fosela</dc:creator>
      <pubDate>Sun, 20 Sep 2020 01:10:05 +0000</pubDate>
      <link>https://dev.to/manufosela/javascript-proxy-algunos-usos-practicos-1hie</link>
      <guid>https://dev.to/manufosela/javascript-proxy-algunos-usos-practicos-1hie</guid>
      <description>&lt;p&gt;Este post está basado en una mezcla de la traducción de los post del &lt;a href="https://www.arbazsiddiqui.me/javascript-proxies-real-world-use-cases/" rel="noopener noreferrer"&gt;blog de Arbaz Siddiqui&lt;/a&gt;, y el &lt;a href="https://blog.logrocket.com/practical-use-cases-for-javascript-es6-proxies/" rel="noopener noreferrer"&gt;blog de Eslam Hefnawy&lt;/a&gt;, &lt;a href="https://javascript.info/proxy" rel="noopener noreferrer"&gt;javascrip.info&lt;/a&gt;&lt;br&gt;
y del post en español de &lt;a href="https://www.todojs.com/introduccion-a-proxy-de-es6/" rel="noopener noreferrer"&gt;TodoJS&lt;/a&gt; y por su puesto de la documentación oficial en &lt;a href="https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Proxy" rel="noopener noreferrer"&gt;MDN&lt;/a&gt; &lt;/p&gt;
&lt;h1&gt;
  
  
  Introducción a Proxy
&lt;/h1&gt;

&lt;p&gt;En términos de programación, proxy es cualquier entidad que actúa en nombre de alguna otra entidad. Un servidor proxy se encuentra entre un cliente y un servidor y actúa como cliente para el servidor y viceversa. El trabajo de cualquier proxy es interceptar las solicitudes / llamadas entrantes y reenviarlas en sentido ascendente. Esta intercepción permite que el proxy agregue lógica y cambie el comportamiento de las solicitudes entrantes y salientes.&lt;/p&gt;

&lt;p&gt;El proxy Javascript es algo muy similar, se encuentra entre su objeto real y el código que intenta acceder a este objeto.&lt;br&gt;
Nos permite hacer metaprogramación dinámica (metaprogramación: programas que escriben o manipulan otros programas).&lt;/p&gt;

&lt;p&gt;Va a permitirnos interceptar las operaciones con objetos y sus propiedades de tal forma que podemos redefinir el comportamiento para cada una de estas acciones&lt;/p&gt;

&lt;p&gt;De acuerdo con los documentos web de MDN:&lt;/p&gt;

&lt;p&gt;El objeto Proxy se usa para definir comportamientos personalizados para operaciones fundamentales (por ejemplo, búsqueda de propiedades, asignación, enumeración, invocación de funciones, etc.).&lt;/p&gt;
&lt;h1&gt;
  
  
  Terminologías
&lt;/h1&gt;

&lt;p&gt;Hay tres términos que necesitamos saber antes de poder implementar un proxy:&lt;/p&gt;
&lt;h3&gt;
  
  
  Objetivo (target)
&lt;/h3&gt;

&lt;p&gt;Target es el objeto real que nuestro proxy interceptará. Este puede ser cualquier objeto de JavaScript.&lt;/p&gt;
&lt;h3&gt;
  
  
  Manejador (handler)
&lt;/h3&gt;

&lt;p&gt;El manejador es un objeto donde viven todas las trampas. &lt;br&gt;
Cada elemento de este objeto tiene como valor una función que implementa una trampa.&lt;/p&gt;
&lt;h3&gt;
  
  
  Trampas (trap)
&lt;/h3&gt;

&lt;p&gt;Las trampas son métodos que interceptarán la llamada al destino cuando se llama a una propiedad o método. Hay muchas trampas definidas que se pueden implementar.&lt;br&gt;
Los métodos que se pueden usar como trampas son:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Object methods:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;getPrototypeOf()&lt;/li&gt;
&lt;li&gt;setPrototypeOf()&lt;/li&gt;
&lt;li&gt;isExtensible()&lt;/li&gt;
&lt;li&gt;preventExtensions()&lt;/li&gt;
&lt;li&gt;getOwnPropertyDescriptor()&lt;/li&gt;
&lt;li&gt;ownKeys()&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Property getters/setters:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;has()&lt;/li&gt;
&lt;li&gt;get()&lt;/li&gt;
&lt;li&gt;set()&lt;/li&gt;
&lt;li&gt;deleteProperty()&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Function methods:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;apply()&lt;/li&gt;
&lt;li&gt;construct()&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Ejemplo básico:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//movie is a target&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;movie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Pulp Fiction&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;director&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Quentin Tarantino&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;//this is a handler&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//get is a trap&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;director&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;God&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;actor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John Travolta&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;movieProxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;movieProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;director&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//God&lt;/span&gt;

&lt;span class="nx"&gt;movieProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tim Roth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;movieProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Uma Thurman&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;movieProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//John Travolta&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;movieProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//Uma Thurman&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;El resultado de la ejecución del código anterior será:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;God
John Travolta
Uma Thurman
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En este ejemplo, nuestro objeto target era movie, implementamos un manejador con dos trampas: un get y un set.&lt;br&gt;
Agregamos la lógica de que si estamos accediendo a la clave director, deberíamos devolver la cadena en God en lugar del valor real.&lt;br&gt;
Del mismo modo, agregamos una trampa en el método set que interceptará todas las escrituras en el objeto de destino y cambiará el valor a John Travolta si la clave es actor.&lt;/p&gt;

&lt;p&gt;Las posibilidades son infinitas&lt;/p&gt;
&lt;h1&gt;
  
  
  Casos de uso del mundo real
&lt;/h1&gt;

&lt;p&gt;Aunque no es tan conocido como otras características de ES2015, Proxy tiene muchos usos. &lt;br&gt;
Veremos escenarios del mundo real donde podemos usar proxies.&lt;/p&gt;
&lt;h2&gt;
  
  
  Validaciones
&lt;/h2&gt;

&lt;p&gt;Como podemos interceptar las escrituras en un objeto, podemos hacer una validación del valor que estamos tratando de establecer en el objeto.&lt;br&gt;&lt;br&gt;
Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;houses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Stark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Lannister&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;house&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;houses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`House &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; does not belong to allowed &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;houses&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gotCharacter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;gotCharacter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jamie&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;gotCharacter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;house&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Lannister&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gotCharacter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;gotCharacter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Oberyn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;gotCharacter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;house&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Martell&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La ejecución del código anterior dará como resultado lo siguiente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ name: 'Jamie', house: 'Lannister' }
Error: House Martell does not belong to allowed Stark,Lannister
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En este ejemplo, restringimos que el valor permitido para la propiedad &lt;strong&gt;house&lt;/strong&gt; solo pueda ser una de las casas permitidas. Incluso podemos usar este enfoque para crear objetos de solo lectura, todo lo que necesitamos hacer es lanzarlo dentro de la trampa set.&lt;/p&gt;

&lt;h2&gt;
  
  
  Efectos secundarios
&lt;/h2&gt;

&lt;p&gt;Podemos usar proxies para crear efectos secundarios en una propiedad de lectura / escritura. La idea es activar alguna función si se accede o se escribe una propiedad en particular. &lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sendEmail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sending email after task completion&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;complete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ...otras tareas que al final llevan a poner un estado...&lt;/span&gt;
&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;complete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La ejecución del código anterior dará como resultado el siguiente resultado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sending email after task completion
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aquí estamos interceptando la escritura de la propiedad 'status' y si status está completa, estamos ejecutando una función de efectos secundarios.&lt;/p&gt;

&lt;h2&gt;
  
  
  Almacenamiento en caché
&lt;/h2&gt;

&lt;p&gt;Como podemos interceptar el acceso a las propiedades del objeto, podemos construir en cachés de memoria para devolver solo valores de un objeto si no ha caducado. &lt;/p&gt;

&lt;p&gt;Por ejemplo :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cacheTarget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ttl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CREATED_AT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isExpired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;CREATED_AT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ttl&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;isExpired&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cacheTarget&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La ejecución del código anterior dará como resultado el siguiente resultado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;25
25 // a los 4 segundos
undefined // a los 6 segundos
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aquí hemos creado una función que devuelve un Proxy. El manejador de ese Proxy primero comprueba si el objeto ha caducado o no. Podemos ampliar esta funcionalidad para tener TTL basados ​​en cada clave.&lt;br&gt;
Otra aproximación puede ser usar ese TTL para pedir a una API (fetch) los datos una vez caducado.&lt;/p&gt;
&lt;h2&gt;
  
  
  Observar cambios en objetos
&lt;/h2&gt;

&lt;p&gt;Como podemos interceptar el acceso a las propiedades del objeto, podemos crear nuevos elementos en DOM y renderizarlos cuando detectamos que una propiedad cambia o si se añade una nueva.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// NOTA IMPORTANTE!! &lt;/span&gt;
&lt;span class="c1"&gt;// En el DOM tenemos un div con id=“salida”&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;salida&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;salida&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;salida&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;observables&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;handlerObservables&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;Reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;deleteProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;Reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleteProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observables&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handlerObservables&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;observables&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prueba&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;valor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// observables['otro'] = 'otro valor';&lt;/span&gt;
&lt;span class="c1"&gt;// delete observables['prueba'];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si añadimos al objeto observables nuevos elementos, estos se irán añadiendo en el DOM dentro de la capa con id salida.&lt;br&gt;
Si eliminamos del objeto elementos, estos se irán eliminando también del DOM.&lt;/p&gt;
&lt;h2&gt;
  
  
  Data binding
&lt;/h2&gt;

&lt;p&gt;El enlace de datos suele ser difícil de lograr debido a su complejidad. El uso de proxies para lograr un enlace de datos bidireccional se puede ver en algunas “librerías” MVC en JavaScript, donde un objeto se modifica cuando el DOM sufre un cambio.&lt;/p&gt;

&lt;p&gt;En pocas palabras, el binding de datos es una técnica que une varias fuentes de datos para sincronizarlas.&lt;/p&gt;

&lt;p&gt;Supongamos que hay un con el id de username.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type = &lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;id = &lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Digamos que se desea mantener el valor de esta entrada sincronizado con una propiedad de un objeto.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="na"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nombre de usuario&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;valor&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Es bastante fácil modificar el valor de inputState cuando el valor del input cambia escuchando el evento ‘change’ del input y luego actualizando el valor de inputState. Sin embargo, lo contrario, actualizar el input cuando el valor de inputState se modifica, es más complicado a priori.&lt;/p&gt;

&lt;p&gt;Un Proxy puede ayudar a lograrlo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nb"&gt;Reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`#&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John Doe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="c1"&gt;// 'John Doe' will be printed for both&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;De esta manera, cuando haya cambio en inputState, el input reflejará el cambio que se ha realizado.&lt;br&gt;
Combinado con escuchar el evento ‘change’, esto producirá doble data binding simple de input e inputState.&lt;/p&gt;

&lt;p&gt;Si bien este es un caso de uso válido, generalmente no se recomienda por performance.&lt;/p&gt;
&lt;h2&gt;
  
  
  Convertir un array de objetos en agrupable
&lt;/h2&gt;

&lt;p&gt;Este quizás sea el ejemplo más complejo, donde se anidan dos Proxy para poder agrupar por el campo que indiquemos.&lt;/p&gt;

&lt;p&gt;Partimos del siguiente json con datos sobre características de procesadores:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;procesadores&lt;/span&gt;&lt;span class="mi"&gt;2020&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"procesador"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Athlon 200GE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"nucleos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"hilos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.2GHz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-max"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.2GHz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"precio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"66.18 €"&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"procesador"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Core i3-9100F"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"nucleos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"hilos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.6 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-max"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4.2 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"precio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"67.99 €"&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"procesador"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ryzen 3 3100"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"nucleos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"hilos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.6 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-max"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.9 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"precio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"105.58 €"&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"procesador"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ryzen 5 2600X"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"nucleos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"hilos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.6 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-max"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4.2 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"precio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"136.35 €"&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"procesador"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Core i5-10400F"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"nucleos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"hilos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.9 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-max"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4.3 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"precio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"149.89 €"&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"procesador"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ryzen 5 3600"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"nucleos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"hilos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.6 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-max"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4.2 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"precio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"200.80 €"&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"procesador"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ryzen 7 2700X"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"nucleos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"hilos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"16"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.7 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-max"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4.3 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"precio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"207.59 €"&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"procesador"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Core i7-10700K"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"nucleos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"hilos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"16"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.8 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-max"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5.1 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"precio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"384.90 €"&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"procesador"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ryzen 7 3700X"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"nucleos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"hilos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"16"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.6 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-max"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4.4 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"precio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"309.95 €"&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"procesador"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Core i9-10850K"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"nucleos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"hilos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.6 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-max"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5.2 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"precio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"486.00 €"&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"procesador"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ryzen 9 3900X"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"nucleos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"hilos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.8 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-max"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4.6 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"precio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"443.90 €"&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"procesador"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ryzen 9 3950X"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"nucleos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"16"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"hilos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"32"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.5 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-max"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4.7 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"precio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"758.87 €"&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"procesador"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ryzen Threadripper 3970X"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"nucleos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"32"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"hilos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"64"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.7 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"frecuencia-max"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4.5 Ghz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"precio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2099.00 €"&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si queremos poder agrupar por los campos de los objetos del array este sería el código.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;groupable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="c1"&gt;// Comprueba que la colección sea un array&lt;/span&gt;
 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The input collection is not an Array&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;grouped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
 &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;groupBy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;configurable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;enumerable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;groupBy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="c1"&gt;// si la propiedad a agrupar no existe devolver []&lt;/span&gt;
           &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;hasOwnProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no encontrado&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
           &lt;span class="p"&gt;}&lt;/span&gt;
           &lt;span class="c1"&gt;// caso contrario, agrupar por la propiedad&lt;/span&gt;
           &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
           &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;groupBy&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
           &lt;span class="nx"&gt;grouped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
           &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
             &lt;span class="p"&gt;}&lt;/span&gt;
             &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]].&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
             &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
           &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
           &lt;span class="nx"&gt;grouped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
           &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;grouped&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="p"&gt;});&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;datasource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;groupable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;procesadores2020&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;datasource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;groupBy&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hilos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;datasource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;groupBy&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;frecuencia-max&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cuando declaramos el array como “groupable” llamando a la función con el mismo nombre y pasandole el array, lo primero que hace es crear una nueva propiedad llamada “groupBy”, convirtiendo el array y devuelve un Proxy que intercepta el get del array.&lt;br&gt;
Podemos decir que hemos ampliado los métodos del array.&lt;/p&gt;

&lt;p&gt;De esta manera si llamamos a cualquier propiedad 0, 1, 2… devolverá el objeto correspondiente a esa posición. &lt;br&gt;
Si llamamos a groupBy nos devuelve otro Proxy que tiene otra trampa en get de manera que a partir del campo que recibe, recorre el array y los agrupa por el campo pasado  y devuelve el array agrupado.&lt;/p&gt;

&lt;p&gt;Esta manera es la manera de añadir funcionalidad a objetos de javascript sin tener que tocar su 'prototipo'.&lt;/p&gt;

&lt;p&gt;Por ejemplo, en String tenemos el método toLowercase() y toUppercase() pero no tenemos el método capitalize(). Si queremos añadir a String el método capitalize podemos hacerlo modificando la cadena protypica del tipo primitivo string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;   &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;capitalize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; 
     &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;buenos días&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capitalize&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// Buenos días&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;De esta manera, todos los String que se creen a partir de esa declaración tendrán un nuevo método 'capitalize' que convierte la primera letra en mayúscula.&lt;/p&gt;

&lt;p&gt;Si esto lo hacemos mediante Proxy, al no ser String un objeto sino un tipo primitivo, tenemos que convertir el String en Object, con lo que perdemos los métodos de String:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addCapitalize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arrStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;arrStr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;capitalize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;arrStr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arrStr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;arrStr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arrStr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arrStr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;capitalize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;saludo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;addCapitalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;buenos días&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;saludo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capitalize&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;saludo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// ERROR: perdemos el resto de métodos de String...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con lo que está no parece ser la manera de ampliar métodos en tipos primitivos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inconvenientes de los Proxies
&lt;/h3&gt;

&lt;p&gt;Si bien los proxies son bastante “mágicos”, hay algunos inconvenientes con ellos por que debemos tener cuidado.&lt;/p&gt;

&lt;p&gt;El rendimiento puede tener un impacto drástico cuando se usan muchos proxies y, por lo tanto, debe evitarse al escribir un código donde el rendimiento sea crítico.&lt;br&gt;
Dado un objeto, no hay forma de saber si se trata de un objeto Proxy u objetivo.&lt;br&gt;
Por último, los servidores Proxy no necesariamente conducen a un código muy limpio y fácil de entender.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusión
&lt;/h2&gt;

&lt;p&gt;Los proxies son increíblemente poderosos y se pueden usar y abusar para una amplia gama de cosas.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>es6</category>
      <category>frontend</category>
    </item>
  </channel>
</rss>
