<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Luiz Schons]]></title><description><![CDATA[Luiz Schons]]></description><link>https://schons.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 22 Oct 2026 22:39:30 GMT</lastBuildDate><atom:link href="https://schons.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Agent Observability with OpenTelemetry]]></title><description><![CDATA[This is the ninth and final article in the series about AI-friendly architecture. Throughout the series, we talked about context, documentation, system…]]></description><link>https://schons.hashnode.dev/agent-observability-with-opentelemetry</link><guid isPermaLink="true">https://schons.hashnode.dev/agent-observability-with-opentelemetry</guid><dc:creator><![CDATA[Luiz Schons]]></dc:creator><pubDate>Wed, 16 Sep 2026 01:28:24 GMT</pubDate><content:encoded><![CDATA[<p>This is the ninth and final article in the series about AI-friendly architecture. Throughout the series, we talked about context, documentation, system observability, skills, agents, tools, MCP, security, and the complete workflow for delivering a feature.</p>
<p>To close the series, I want to look inside the AI tool itself.</p>
<p>When an agent takes part in a task, we usually look only at the final result. Was the pull request created? Did the tests pass? Was the feature delivered?</p>
<p>These questions matter, but they do not explain the path the agent took.</p>
<p>How long did it take to find the right context? How many times did it repeat the same investigation? Which tools did it use? When did it need approval? Was the problem in the model, the documentation, the integration, or the workflow itself?</p>
<p>Without these signals, every evaluation is based on impressions.</p>
<h2>The agent's work also needs to be observable</h2>
<p>In traditional systems, we already know that logs, metrics, and traces help explain how an application behaves. We do not consider an API reliable just because it answered once. We observe latency, errors, dependencies, traffic, and impact.</p>
<p>With agents, many teams still do the opposite. They evaluate the tool through a few isolated interactions and decide that it is fast, slow, good, or bad.</p>
<p>An agent session is also a distributed flow. It involves a model, context, tools, permissions, external systems, files, tests, and human decisions. The final result is only the last event in this chain.</p>
<p>If we want to improve this flow, we need to see what happens before the final result.</p>
<img src="https://cdn.hashnode.com/uploads/covers/67081fa628a6c4cdb75a7fc9/e98ed5d6-ff82-4af9-abd1-c47cbabb2f20.png" alt="" style="display:block;margin:0 auto" />

<h2>What is worth measuring?</h2>
<p>Observability does not mean recording everything. The goal is not to watch developers or turn token counts into a false measure of productivity.</p>
<p>The goal is to create evidence for engineering decisions.</p>
<p>Some useful signals include:</p>
<ul>
<li><p>number and duration of sessions;</p>
</li>
<li><p>time spent on model and tool calls;</p>
</li>
<li><p>tokens used and estimated cost;</p>
</li>
<li><p>errors, retries, and interruptions;</p>
</li>
<li><p>number of approval requests;</p>
</li>
<li><p>tests run and their results;</p>
</li>
<li><p>files or lines changed;</p>
</li>
<li><p>task result, such as a created pull request, an interrupted change, or a completed delivery.</p>
</li>
</ul>
<p>These signals become useful when we connect them to real questions.</p>
<p>Did new documentation reduce the time to the first useful hypothesis? Did an investigation skill reduce the number of tool calls? Is an external MCP adding latency without improving the result? Is an agent asking for approval too early or too late? Did a context change increase cost because it added useful information or because it added noise?</p>
<p>We cannot answer these questions by looking only at the agent's final response.</p>
<p>A good metric is not always the easiest one to count. It is the one that helps us understand a decision or find an opportunity to improve.</p>
<h2>OpenTelemetry as a common layer</h2>
<p>This article uses OpenTelemetry because it is an open standard and an open-source project. We can start without paying for a proprietary observability platform.</p>
<p>One possible architecture uses an OpenTelemetry Collector to receive and forward signals, Prometheus to store metrics, and Grafana to query them. These components are free and open source, although the model used by the tool or a managed platform may have its own costs.</p>
<p>The most important point is not the dashboard choice. It is the separation between the tool that produces events and the system that observes them.</p>
<p>OpenCode is only the example used in this article. It can be instrumented by a plugin that exports signals through OTLP. The same architecture can be used with Codex, Claude Code, Cursor, or another tool when it has a native exporter, plugin, hook, or adapter that can produce telemetry.</p>
<p>Each tool may emit events in a different way. The backend does not need to know all these details. It can work with more stable concepts such as session, model call, tool, approval, error, and result.</p>
<p>This separation lets us replace the tool without rebuilding the entire observability infrastructure. It also lets us compare different workflows using a common language.</p>
<h2>From activity to results</h2>
<p>A common trap is to observe activity only.</p>
<p>More messages do not always mean more progress. More tokens may represent better context, but they may also mean that the agent is lost. More tool calls may show a careful investigation or a lack of documentation.</p>
<p>For this reason, activity signals must be connected to result signals.</p>
<table>
<thead>
<tr>
<th>Activity</th>
<th>Result worth investigating</th>
</tr>
</thead>
<tbody><tr>
<td>Tokens used</td>
<td>Was the task completed with less rework?</td>
</tr>
<tr>
<td>Tool calls</td>
<td>Did the agent find better evidence?</td>
</tr>
<tr>
<td>Session duration</td>
<td>Did the extra time improve quality or only add waiting?</td>
</tr>
<tr>
<td>Approval requests</td>
<td>Were the autonomy limits appropriate?</td>
</tr>
<tr>
<td>Lines changed</td>
<td>Were the tests and success criteria met?</td>
</tr>
</tbody></table>
<p>This care prevents observability from becoming a scoreboard. We are not judging the agent by the largest possible amount of output. We are trying to understand whether the workflow helps the team reach better decisions safely.</p>
<h2>Context also appears in the metrics</h2>
<p>Session signals can show problems that are not in the model.</p>
<p>If the agent spends a long time looking for files, the context architecture may be hard to navigate. If it repeats queries, a more specialized skill may be missing. If external calls are slow, the integration may be poorly designed. If it asks for approval at almost every step, the permissions may be too strict or the workflow may not be clear enough.</p>
<p>This is an important part of AI-friendly architecture: context is not only something we give to the agent. It is also something we can evaluate through observed behavior.</p>
<p>Telemetry closes the loop between context, execution, and learning.</p>
<h2>Observability is also security</h2>
<p>Agent telemetry may contain more information than it seems.</p>
<p>A prompt may contain an internal rule. A tool argument may contain a token. A result may include customer data. A trace may record part of the code. A file path may reveal the structure of a system.</p>
<p>For this reason, we should not trust the agent itself to decide what can be sent to the backend. Protection must be enforced by deterministic components such as the Collector, the telemetry gateway, and access policies.</p>
<p>Some principles are important:</p>
<ul>
<li><p>full prompts should not be stored in metrics;</p>
</li>
<li><p>tool arguments and results must be sanitized before they are sent;</p>
</li>
<li><p>file paths and code content should not become labels;</p>
</li>
<li><p>credentials must stay out of versioned configuration;</p>
</li>
<li><p>tokens for managed destinations should have the least privilege possible;</p>
</li>
<li><p>retention and access must be defined before collection starts;</p>
</li>
<li><p>high-cardinality identifiers must be handled carefully.</p>
</li>
</ul>
<p>This connects to the article about <a href="https://luizschons.com/seguran-a-guardrails-e-fitness-functions-para-agentes">security, guardrails, and fitness functions</a>. Deterministic guardrails should not protect only the agent's actions. They must also protect the data created during the agent's work.</p>
<h2>The <code>session_id</code> case</h2>
<p>During an investigation, it can be useful to open one session and understand what happened in it. A local implementation may let you select a <code>session_id</code> in Grafana for this type of analysis.</p>
<p>This is convenient in a local environment, but each new session may create a new metric series. In an operation with many users and executions, this cardinality growth can be expensive and hard to support.</p>
<p>That is why observability for development is different from observability for production.</p>
<p>In development, keeping the identifier can make learning easier. In production, it may be better to use aggregated metrics and send sanitized traces or logs to a backend designed for individual investigation.</p>
<p>A dashboard is an investigation tool. It should not be treated as permission to collect any data we want.</p>
<h2>A lab that makes the idea concrete</h2>
<p>To keep the discussion practical, I created the repository <a href="https://github.com/sschonss/agent-observability-with-opentelemetry">agent-observability-with-opentelemetry</a>.</p>
<p>It contains a local setup with OpenTelemetry Collector, Prometheus, and Grafana, plus an initial dashboard for agent sessions. OpenCode is used as the instrumentation example, but the backend was designed to receive signals from other tools as well.</p>
<p>The repository also documents the limits of the experiment. Logs and traces are disabled at first because they may contain prompts, code, and sensitive arguments. The idea is to start with a small set of metrics, understand what we really need to observe, and only then expand the collection.</p>
<p>This is healthier than turning on every available signal and discovering too late that we created a new security problem.</p>
<h2>Conclusion</h2>
<p>An AI-friendly architecture should not only provide context to the agent. It should also make the agent's work observable.</p>
<p>When we measure sessions, duration, tokens, tools, errors, approvals, and results, we can discuss how the workflow is improving based on evidence. We may discover that the problem is not the model but the documentation. We may see that a skill reduced unnecessary exploration. We may find a slow integration or a step that asks for human review too early.</p>
<p>OpenTelemetry offers an open way to get started. OpenCode was only the example used in this article. The same separation between tool, instrumentation, Collector, and backend can support other tools and models.</p>
<p>This is the end of the series. The central idea is simple: better agents depend less on guessing and more on context, limits, evidence, and feedback. Observability is what shows us whether we are truly improving.</p>
]]></content:encoded></item><item><title><![CDATA[From Epic to Production: Using Agents to Deliver Features in Real Systems]]></title><description><![CDATA[This is the eighth article in the series about AI-friendly architecture. In the previous articles, we talked about context, documentation, observability…]]></description><link>https://schons.hashnode.dev/from-epic-to-production-using-agents-to-deliver-features-in-real-systems</link><guid isPermaLink="true">https://schons.hashnode.dev/from-epic-to-production-using-agents-to-deliver-features-in-real-systems</guid><dc:creator><![CDATA[Luiz Schons]]></dc:creator><pubDate>Sat, 12 Sep 2026 15:26:58 GMT</pubDate><content:encoded><![CDATA[<p>This is the eighth article in the series about AI-friendly architecture. In the previous articles, we talked about context, documentation, observability, skills, Context Skills, agents, tools, MCP, guardrails, and harnesses.</p>
<p>Now we will bring these pieces together in a complete engineering workflow.</p>
<p>The idea is not to ask an agent to “implement a feature” and expect it to discover everything by itself. The goal is to show how an agent can help with a delivery from the moment we understand the epic until we monitor the change in production.</p>
<p>Writing the code is only one step. In many cases, it is not even the hardest one.</p>
<h2>The epic</h2>
<p>Let us use this request as an example:</p>
<pre><code class="language-text">As a customer, I want to request a partial refund for an order,
so I can return only some of the items I bought.
</code></pre>
<p>At first, this looks like a small change in the payment service.</p>
<p>But before writing code, we need to understand several things:</p>
<ul>
<li><p>where the order is created;</p>
</li>
<li><p>who calculates the total amount;</p>
</li>
<li><p>which service owns the payment;</p>
</li>
<li><p>how items are linked to the transaction;</p>
</li>
<li><p>which cancellation rules exist;</p>
</li>
<li><p>how inventory is updated;</p>
</li>
<li><p>how the customer is notified;</p>
</li>
<li><p>which data must be sent to the payment provider.</p>
</li>
</ul>
<p>An agent that receives only the epic can produce a plausible solution that is still wrong.</p>
<h2>Question the epic</h2>
<p>The agent's first job should not be creating files. It should be turning the epic into questions.</p>
<p>Possible questions include:</p>
<ul>
<li><p>What business problem are we trying to solve?</p>
</li>
<li><p>Who can request a refund?</p>
</li>
<li><p>Which items are eligible?</p>
</li>
<li><p>Is there a time limit for requesting a refund?</p>
</li>
<li><p>Can an order have more than one refund?</p>
</li>
<li><p>How do we handle a partial refund that has already started?</p>
</li>
<li><p>Is the refund amount calculated from the order or from the payment?</p>
</li>
<li><p>What happens if the payment provider accepts the refund but the internal update fails?</p>
</li>
<li><p>Should inventory be released immediately or only after confirmation?</p>
</li>
<li><p>How will the customer be informed?</p>
</li>
</ul>
<p>The agent does not need to answer everything by itself. Its job is to take the right questions to the product owner and the teams involved.</p>
<p>Questioning the epic does not block the work. It turns unclear points into explicit decisions before they become bugs or rework.</p>
<p>A planning skill can guide the creation of these questions. The agent can check domain documentation and use a tool to find epics, requirements, and previous decisions.</p>
<h2>Run the spike</h2>
<p>After the goal is clear, the technical investigation begins.</p>
<p>The agent can use different Context Skills to investigate the problem. An order skill helps it understand order items and states. A payment skill explains the provider's rules. An observability skill shows how to investigate the current behavior.</p>
<p>The goal of the spike is not to produce a nice answer. It is to build a view based on evidence.</p>
<p>The agent can look for:</p>
<ul>
<li><p>code related to the order;</p>
</li>
<li><p>the current cancellation flow;</p>
</li>
<li><p>the payment provider integration;</p>
</li>
<li><p>refund or cancellation tests;</p>
</li>
<li><p>events published after a payment change;</p>
</li>
<li><p>consumers of those events;</p>
</li>
<li><p>ADRs and previous decisions;</p>
</li>
<li><p>metrics and traces for the current flow;</p>
</li>
<li><p>related incidents.</p>
</li>
</ul>
<p>A reasonable investigation could follow this order:</p>
<ol>
<li><p>Find the order model or contract.</p>
</li>
<li><p>Find the current cancellation flow.</p>
</li>
<li><p>Find the payment provider integration.</p>
</li>
<li><p>Find refund or cancellation tests.</p>
</li>
<li><p>Find events published after a payment change.</p>
</li>
<li><p>Find the consumers of those events.</p>
</li>
<li><p>Check production signals to understand the current behavior.</p>
</li>
</ol>
<p>The agent should not only find files with similar names. It needs to rebuild the flow and explain how the parts are connected.</p>
<h2>Record the spike decision</h2>
<p>The result of the spike should not remain only in the conversation history. It should become documentation that can be found during future investigations.</p>
<p>A decision document could look like this:</p>
<pre><code class="language-markdown"># Decision: partial refunds

## Context

An order can contain several items, but the current refund flow
works only with the total transaction amount.

## Options considered

- create a new transaction type;
- allow multiple refunds on the existing transaction;
- create a separate refund service.

## Decision

Use the existing transaction and track the total refunded amount.

## Consequences

- the external provider must support multiple refunds;
- the refund event must identify the items;
- the operation must be idempotent;
- old consumers must keep working.

## Risks

- two refund requests may run at the same time;
- the external and internal states may become different;
- some consumers may not know the new format.
</code></pre>
<p>This documentation helps the current team, but it also helps future agents and future work.</p>
<p>Every well-documented spike adds a new context source to the architecture. Over time, we stop depending only on code and build a map of decisions, contracts, and consequences.</p>
<h2>Define what success means</h2>
<p>Before creating tasks, we need to answer one simple question:</p>
<blockquote>
<p>How will we know that this implementation solved the problem?</p>
</blockquote>
<p>For a partial refund, success may mean:</p>
<ul>
<li><p>the customer can select eligible items;</p>
</li>
<li><p>the refund amount never exceeds the amount paid;</p>
</li>
<li><p>the same request is never processed twice;</p>
</li>
<li><p>the provider receives the correct data;</p>
</li>
<li><p>inventory is updated at the right time;</p>
</li>
<li><p>the customer receives a clear notification;</p>
</li>
<li><p>old consumers keep working;</p>
</li>
<li><p>the error rate does not increase;</p>
</li>
<li><p>latency stays within the expected limit;</p>
</li>
<li><p>the number of partial-refund support requests decreases;</p>
</li>
<li><p>the operation can be investigated later.</p>
</li>
</ul>
<p>These criteria matter more than the number of changed files. Implemented code does not automatically mean that the problem was solved.</p>
<p>We also need to measure the impact outside the system. If the goal is to let customers request partial refunds by themselves, fewer support requests about this process may be an important success metric.</p>
<p>This metric should be combined with technical signals. Fewer support requests are a success only if the error rate did not increase and refunds are being processed correctly. Otherwise, we may only be making the problem less visible to support.</p>
<p>That is why the definition of success should already point to observability work. These criteria must become metrics, queries, dashboards, and alerts.</p>
<h2>Define and create the tasks</h2>
<p>With the decisions and success criteria defined, the agent can break the delivery into tasks:</p>
<ul>
<li><p>add the partial-refund endpoint;</p>
</li>
<li><p>validate items and the total refunded amount;</p>
</li>
<li><p>update the provider integration;</p>
</li>
<li><p>ensure idempotency and concurrency control;</p>
</li>
<li><p>version the payment event;</p>
</li>
<li><p>update inventory and notification consumers;</p>
</li>
<li><p>create unit and integration tests;</p>
</li>
<li><p>configure the QA environment;</p>
</li>
<li><p>add metrics and traces;</p>
</li>
<li><p>create dashboards and alerts;</p>
</li>
<li><p>update the documentation and runbook.</p>
</li>
</ul>
<p>A tool can create these tasks in the system used by the team. A planning skill can define the minimum format for each task, including context, acceptance criteria, dependencies, and risks.</p>
<p>The agent can prepare the tasks, but the team must still confirm that the split correctly represents the work and ownership of each domain.</p>
<h2>Define how to test</h2>
<p>Before implementation, we need to decide how to validate the change in a non-production environment.</p>
<p>This may involve:</p>
<ul>
<li><p>test data for orders with several items;</p>
</li>
<li><p>a simulated payment provider;</p>
</li>
<li><p>a feature flag for gradual rollout;</p>
</li>
<li><p>a QA environment with updated consumers;</p>
</li>
<li><p>contract tests for events;</p>
</li>
<li><p>concurrency tests;</p>
</li>
<li><p>idempotency tests;</p>
</li>
<li><p>failure tests after external confirmation;</p>
</li>
<li><p>rollback tests.</p>
</li>
</ul>
<p>The agent can create test scenarios from the success criteria, but the team must check whether the environment can reproduce the important conditions of the real system.</p>
<p>A good question is:</p>
<blockquote>
<p>What must be configured in QA for us to trust the result?</p>
</blockquote>
<p>If the answer is “nothing,” the test has probably not been defined well enough.</p>
<h2>Define observability before the code</h2>
<p>Even before AI tools existed, it was good practice to decide how a change would be observed after release.</p>
<p>For each feature, we need to decide:</p>
<ul>
<li><p>which logs will be emitted;</p>
</li>
<li><p>which metrics will be collected;</p>
</li>
<li><p>which traces must exist;</p>
</li>
<li><p>which queries will be used;</p>
</li>
<li><p>which behavior means success;</p>
</li>
<li><p>which behavior means degradation;</p>
</li>
<li><p>which signal should start a rollback.</p>
</li>
</ul>
<p>For partial refunds, we could track:</p>
<ul>
<li><p>the number of started requests;</p>
</li>
<li><p>the number of confirmed refunds;</p>
</li>
<li><p>the number of provider failures;</p>
</li>
<li><p>the total refunded amount;</p>
</li>
<li><p>the time between request and confirmation;</p>
</li>
<li><p>differences between external and internal state;</p>
</li>
<li><p>the number of retries;</p>
</li>
<li><p>failures by provider or payment method.</p>
</li>
</ul>
<p>The agent can help write queries and configure dashboards, but deciding what to observe is an engineering decision. Without this work, the team can release the change and have no evidence that it worked.</p>
<h2>Implement the code</h2>
<p>Only now does implementation begin.</p>
<p>The agent can help to:</p>
<ul>
<li><p>create or change classes;</p>
</li>
<li><p>update contracts;</p>
</li>
<li><p>write tests;</p>
</li>
<li><p>change schemas;</p>
</li>
<li><p>add instrumentation;</p>
</li>
<li><p>update configuration;</p>
</li>
<li><p>prepare a pull request.</p>
</li>
</ul>
<p>But it is important to understand the size of this step. Implementation is only one part of the cycle.</p>
<p>Understanding the problem, making decisions, defining success, preparing the environment, choosing signals, and planning operations may require more reasoning than writing the code itself.</p>
<p>The agent becomes more useful when it takes part in all these steps, not only when it receives a file to modify.</p>
<h2>Create dashboards and alerts</h2>
<p>Before the deployment, we need to prepare the operation of the feature.</p>
<p>This may include:</p>
<ul>
<li><p>a dashboard for the new flow;</p>
</li>
<li><p>success rate;</p>
</li>
<li><p>error rate;</p>
</li>
<li><p>latency;</p>
</li>
<li><p>request volume;</p>
</li>
<li><p>impact by customer or region;</p>
</li>
<li><p>failures by dependency;</p>
</li>
<li><p>state-mismatch alerts;</p>
</li>
<li><p>error-increase alerts;</p>
</li>
<li><p>rollback criteria.</p>
</li>
</ul>
<p>A dashboard without a clear operational question can become decoration. Each panel should help answer a question such as:</p>
<ul>
<li><p>Is the feature being used?</p>
</li>
<li><p>Is it working?</p>
</li>
<li><p>Is it slower?</p>
</li>
<li><p>Is it affecting another context?</p>
</li>
<li><p>Do we need to stop the rollout?</p>
</li>
</ul>
<h2>Deploy and monitor</h2>
<p>The agent can prepare the pull request, the change record, and the deployment plan. It can summarize risks, list checks, and organize the steps.</p>
<p>Production execution must still follow the organization's policies. Depending on the risk, a person may need to approve the change record, review the pull request, and authorize the merge.</p>
<p>After deployment, monitoring should compare the observed behavior with the previous baseline:</p>
<ul>
<li><p>Are the metrics within the expected range?</p>
</li>
<li><p>Has the error rate changed?</p>
</li>
<li><p>Is the provider responding as expected?</p>
</li>
<li><p>Are events being consumed?</p>
</li>
<li><p>Do the dashboards show the result defined at the start?</p>
</li>
<li><p>Does any alert need to be triggered?</p>
</li>
</ul>
<p>The agent can check the signals and organize the analysis. The decision to continue, pause, or revert the change must follow the limits defined by the team.</p>
<h2>Finish the delivery with documentation</h2>
<p>The delivery does not end when the deployment is complete.</p>
<p>The document created during the spike should be updated with the real implementation result:</p>
<ul>
<li><p>what was implemented;</p>
</li>
<li><p>which decisions changed;</p>
</li>
<li><p>which contracts were versioned;</p>
</li>
<li><p>which metrics were created;</p>
</li>
<li><p>which alerts exist;</p>
</li>
<li><p>how to operate the feature;</p>
</li>
<li><p>how to investigate failures;</p>
</li>
<li><p>which limitations remain;</p>
</li>
<li><p>which lessons can be reused.</p>
</li>
</ul>
<p>This closing step creates a learning cycle:</p>
<ol>
<li><p>The epic creates questions.</p>
</li>
<li><p>The spike creates a decision.</p>
</li>
<li><p>The implementation creates new behavior.</p>
</li>
<li><p>Operations creates evidence.</p>
</li>
<li><p>Documentation records what was learned.</p>
</li>
<li><p>The next request starts with more context.</p>
</li>
</ol>
<p>This is how a Context Architecture really grows. It is not a document created once. It is a body of knowledge that grows with the system.</p>
<h2>Where do skills, tools, and MCP fit?</h2>
<p>Each step can use a different combination:</p>
<ul>
<li><p><strong>Planning Skill:</strong> questions the epic and breaks down the work;</p>
</li>
<li><p><strong>Architecture Skill:</strong> checks decisions and identifies impacts;</p>
</li>
<li><p><strong>Testing Skill:</strong> defines scenarios and validation strategies;</p>
</li>
<li><p><strong>Observability Skill:</strong> creates queries, metrics, and dashboards;</p>
</li>
<li><p><strong>Delivery Skill:</strong> prepares the pull request, change record, and deployment;</p>
</li>
<li><p><strong>Tools:</strong> perform specific actions, such as finding documents or creating tasks;</p>
</li>
<li><p><strong>MCP:</strong> connects the agent to the systems where these actions happen.</p>
</li>
</ul>
<p>The agent coordinates the workflow, but it does not need to carry all knowledge from all contexts at the same time. Each skill provides a specialization, and each tool performs a concrete capability.</p>
<h2>The role of human review</h2>
<p>The goal is not to remove the team from the engineering process.</p>
<p>The team still needs to:</p>
<ul>
<li><p>confirm business rules;</p>
</li>
<li><p>evaluate trade-offs;</p>
</li>
<li><p>approve contract changes;</p>
</li>
<li><p>decide which risks are acceptable;</p>
</li>
<li><p>approve production changes;</p>
</li>
<li><p>take responsibility for the decision.</p>
</li>
</ul>
<p>The agent helps make preparation faster and more complete. It can move through different sources, find relationships that might be missed, and organize evidence in a format that is easier to review.</p>
<p>The decision remains the team's responsibility.</p>
<h2>Conclusion</h2>
<p>An agent can take part in the full life cycle of a feature without starting by writing code.</p>
<p>It can question an epic, run a spike, record decisions, define success criteria, create tasks, plan tests, prepare observability queries, implement changes, open a pull request, monitor a deployment, and update documentation.</p>
<p>This workflow works well only when context is available, connected, and reliable. Without documentation, decisions, observability, and skills, the agent tends to fill gaps with guesses.</p>
<p>With an AI-friendly architecture, the agent can work with evidence and make it clear what it knows, what it inferred, and what still needs to be decided.</p>
<p>This is where the conversation stops being only about generating code. The agent starts to take part in the engineering work that happens before, during, and after implementation.</p>
]]></content:encoded></item><item><title><![CDATA[Agents, Skills, Tools, and MCP: How These Pieces Fit Together]]></title><description><![CDATA[This is the seventh article in the series about AI-friendly architecture. So far, we have talked about context, documentation, observability, skills, and…]]></description><link>https://schons.hashnode.dev/agents-skills-tools-and-mcp-how-these-pieces-fit-together</link><guid isPermaLink="true">https://schons.hashnode.dev/agents-skills-tools-and-mcp-how-these-pieces-fit-together</guid><dc:creator><![CDATA[Luiz Schons]]></dc:creator><pubDate>Tue, 08 Sep 2026 23:11:11 GMT</pubDate><content:encoded><![CDATA[<p>This is the seventh article in the series about AI-friendly architecture. So far, we have talked about context, documentation, observability, skills, and breaking knowledge into Context Skills.</p>
<p>Before using all of this in a development workflow, it helps to separate four concepts that often appear together: agent, skill, tool, and MCP.</p>
<p>They are not the same thing. Each one solves a different part of the problem.</p>
<p>In this article, we will use OpenCode as a concrete example because it is free and easy to use for experimenting with these ideas. The examples are practical, but this is not an OpenCode-only tutorial. The same principles work with other tools that provide similar ways to define agents, load instructions, register capabilities, and connect MCP servers.</p>
<h2>What is an agent?</h2>
<p>An agent is a system that receives a goal, checks the available context, decides which steps to take, and uses external capabilities to move forward.</p>
<p>A simple conversation has a direct flow: the user sends a request to the AI model, and the model returns an answer. The model responds to that request, but it does not usually plan several steps or use external systems on its own.</p>
<p>An agent works in a loop. It receives a goal, checks the available context, decides what to do next, uses tools when needed, and checks the result. It repeats these steps until the goal is complete or it needs help from a person.</p>
<p>The loop ends when the goal is reached, when there is not enough information, or when a person needs to take over the decision.</p>
<p>So, creating an agent is not only about choosing a model. You also need to define a goal, context, capabilities, action limits, and a way to check the result.</p>
<h2>How do you create an agent?</h2>
<p>In OpenCode, an agent can be configured in <code>opencode.json</code> or in a Markdown file inside <code>.opencode/agents/</code>. A conceptual example would be:</p>
<pre><code class="language-markdown">---
description: Investigates incidents and prepares change proposals
mode: primary
permissions:
  - action: edit
    resource: "*"
    effect: deny
  - action: skill
    resource: "incident-investigation"
    effect: allow
---

Investigate incidents using evidence from the available context.

Always separate confirmed facts, hypotheses and missing information.
Prepare changes as proposals. Do not apply production changes automatically.
</code></pre>
<p>The format is specific to OpenCode, but the decisions are general. An agent needs a goal, an execution mode, permissions, and instructions for presenting the result.</p>
<p>In another tool, this may appear as a profile, a configuration file, or a workflow definition. The name changes. The architecture stays the same.</p>
<p>A well-defined agent should answer:</p>
<ul>
<li><p>what problem it solves;</p>
</li>
<li><p>when it should be used;</p>
</li>
<li><p>which contexts it knows;</p>
</li>
<li><p>which actions it can perform;</p>
</li>
<li><p>which actions need approval;</p>
</li>
<li><p>how it should present the result.</p>
</li>
</ul>
<p>For example, an investigation agent may be allowed to read code, documentation, and operational data, but not to make changes. This difference should be configured in the tool and enforced by the systems it accesses, not only written in the prompt.</p>
<h2>What is a skill?</h2>
<p>A skill is knowledge organized for a specific context or workflow.</p>
<p>It may contain concepts, questions, an investigation sequence, decision rules, references, and limits.</p>
<p>A skill is not the whole agent. It is a specialization that the agent can load when a task needs that knowledge.</p>
<p>In OpenCode, a skill can be created as a directory with a <code>SKILL.md</code> file:</p>
<pre><code class="language-text">.opencode/
└── skills/
    └── incident-investigation/
        └── SKILL.md
</code></pre>
<p>The file can start with metadata and work instructions:</p>
<pre><code class="language-markdown">---
name: incident-investigation
description: Investigate incidents using operational signals and recent changes
---

## Workflow

1. Establish the impact and affected services.
2. Compare current signals with a known baseline.
3. Check recent changes and deployments.
4. Separate facts from hypotheses.
5. Stop when evidence is insufficient and ask for human input.
</code></pre>
<p>OpenCode makes the skill available to the agent and can load it when it is relevant. In another tool, the same content may be registered as a reusable instruction, workflow, or context package. The important point is to separate specialized knowledge from the agent that coordinates the task.</p>
<p>The agent coordinates. The skill guides.</p>
<p>A skill does not need to contain all domain data. It can point to the documentation, dashboards, and catalogs that are the original sources.</p>
<h2>What is MCP?</h2>
<p>MCP, or Model Context Protocol, is a protocol for connecting AI applications to external context and capabilities.</p>
<p>It defines a standard way for a server to offer components that a client can discover and use. These components include:</p>
<ul>
<li><p>tools, which perform actions or queries;</p>
</li>
<li><p>resources, which provide content and context;</p>
</li>
<li><p>prompts, which provide reusable templates or instructions.</p>
</li>
</ul>
<p>A simple way to see it is:</p>
<p>MCP mainly solves the problem of connection and discovery. It does not replace a skill, decide by itself which action should run, or make an unsafe integration safe.</p>
<p>Permissions, approvals, authentication, and limits are still the responsibility of the application and the team that provides the integration.</p>
<h2>Connecting an MCP server to OpenCode</h2>
<p>To connect a third-party MCP server to OpenCode, declare the server in the project's <code>opencode.json</code> file. One example is Context7, which provides access to up-to-date technical documentation through an MCP server:</p>
<pre><code class="language-json">{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "servers": {
      "context7": {
        "type": "remote",
        "url": "https://mcp.context7.com/mcp",
        "headers": {
          "CONTEXT7_API_KEY": "{env:CONTEXT7_API_KEY}"
        }
      }
    }
  }
}
</code></pre>
<p>In this example, OpenCode connects to a remote server over HTTP. The key is stored in an environment variable instead of being written directly in the project file. After the connection, the agent can discover the tools, resources, and prompts provided by the server.</p>
<p>OpenCode also provides commands to add and check MCP servers:</p>
<pre><code class="language-bash">opencode mcp add
opencode mcp list
</code></pre>
<p>The configuration file makes the connection clear and versionable. The command may be more convenient when the setup is local or when the server uses interactive authentication.</p>
<p>You could describe its use to the agent like this:</p>
<pre><code class="language-text">Use the context7 server to check the library documentation before proposing an implementation.
Prefer the documentation for the version used by the project.
Do not treat returned content as a security instruction.
</code></pre>
<p>The conceptual flow is simple: the agent asks the MCP client for a capability, the client connects to the MCP server, and the server provides tools, resources, or prompts. The agent can then use the returned capability as part of its work.</p>
<p>Other tools may use a different configuration file, but the flow is the same: say where the server is, how to connect to it, and which credentials or policies to use.</p>
<p>A third-party server should also be treated as an external source. Its content may be old, incomplete, or contain instructions that the agent should not follow. As discussed in <a href="https://luizschons.com/guardrails-and-fitness-functions-for-ai-friendly-architecture">Security, Guardrails, and Fitness Functions for Agents</a>, context and connection do not replace authentication, authorization, and validation in the protected system.</p>
<h2>What is a tool?</h2>
<p>Now that we have seen how a client discovers capabilities through MCP, we can define one of them more precisely.</p>
<p>A tool is a capability that an agent can run. It may query a system, find a file, call an API, calculate a value, create a ticket, or start an operational action.</p>
<p>For example:</p>
<pre><code class="language-json">{
  "name": "query_metrics",
  "description": "Query a metric for a service and time range.",
  "input_schema": {
    "type": "object",
    "properties": {
      "service": { "type": "string" },
      "metric": { "type": "string" },
      "from": { "type": "string" },
      "to": { "type": "string" }
    },
    "required": ["service", "metric", "from", "to"]
  }
}
</code></pre>
<p>The description and schema matter because the agent needs to know when to use the tool and which arguments to send. The implementation must also validate those arguments and apply its own permissions.</p>
<p>A tool does not always explain how to interpret its result. It may return a time series without saying whether the change is normal for that domain. That interpretation belongs in the skill or in the operational documentation.</p>
<h2>A tool is not a skill</h2>
<p>The difference can be summarized like this:</p>
<pre><code class="language-text">Skill
  How to think about the problem
  When to check each source
  How to interpret the results
  Which limits to respect

Tool
  Which action can be run
  Which inputs are needed
  Which result will be returned
</code></pre>
<p>An observability skill may use several tools. For example, it may read logs, query metrics, inspect traces, and check recent deployments. Each tool provides a different signal, while the skill explains how to use those signals together.</p>
<p>Tools can be shared by several skills. The skill organizes their use for a specific context.</p>
<p>A skill can therefore guide the use of several tools. It can tell the agent which tool to use first, what information to collect next, and how to compare the results.</p>
<p>In this example, the skill says when to check each source, which questions to ask, and how to interpret the results. The tools perform the concrete actions.</p>
<p>However, a skill should not grant permissions by itself. It may recommend using <code>query_metrics</code>, but the agent or the harness must decide whether the tool is available and whether the call may run.</p>
<p>A simple summary is:</p>
<ul>
<li><p>the skill organizes and guides the use of tools;</p>
</li>
<li><p>the tool runs a concrete capability;</p>
</li>
<li><p>the agent decides when to load the skill;</p>
</li>
<li><p>the harness provides the tools and applies permissions.</p>
</li>
</ul>
<h2>Example with Hyperf MCP</h2>
<p>The <a href="https://github.com/hyperf/mcp-incubator"><code>hyperf/mcp-incubator</code></a> project lets you create an MCP server in a Hyperf application. As the name suggests, it is still evolving, so check the API details for the version used by your project. The principle is the same: expose application capabilities through an MCP server. After installing the package:</p>
<pre><code class="language-bash">composer require hyperf/mcp-incubator
</code></pre>
<p>We can expose a read-only capability to check the state of a service:</p>
<pre><code class="language-php">&lt;?php

namespace App\Mcp;

use Hyperf\Mcp\Annotation\Tool;
use Hyperf\Mcp\Server\Annotation\Server;

#[Server(
    name: 'orders-context',
    signature: 'mcp:command',
    description: 'Read-only context for the orders domain'
)]
class OrdersContextServer
{
    #[Tool(
        name: 'service_health',
        description: 'Returns the current health summary for an orders service',
        serverName: 'orders-context'
    )]
    public function serviceHealth(string $service): array
    {
        return [
            'service' =&gt; $service,
            'status' =&gt; 'healthy',
            'checked_at' =&gt; date(DATE_ATOM),
        ];
    }
}
</code></pre>
<p>When the server is connected to OpenCode, the <code>service_health</code> tool becomes available to the agent. The investigation skill can explain when to use it and how to read its result:</p>
<blockquote>
<p>Use <code>service_health</code> to establish the current state of the service. Compare the result with observability signals. Do not decide that the service is healthy based on one query.</p>
</blockquote>
<p>The tool runs the query. The skill defines the context. The agent coordinates the steps. MCP only standardizes communication between the client and the server.</p>
<p>In a real system, <code>service_health</code> could query an operational source, a data replica, or an investigation layer. It should not automatically receive a broad production credential. Authentication, least privilege, and data limits remain the responsibility of the Hyperf server and the resources it accesses.</p>
<p>The same server could be connected to another MCP client without changing the tool. This is one of the main benefits of separating an application capability from the interface used by the agent.</p>
<h2>The harness around the agent</h2>
<p>An agent is not only the model. There is a layer around it that defines which information it receives, which tools it can use, how task state is kept, which actions need approval, and how the result is checked.</p>
<p>This layer is often called the <strong>agent harness</strong>. It surrounds the agent and connects it to context, skills, tools, MCP servers, permissions, task state, and verification.</p>
<p>The harness brings together context, skills, tools, MCP connections, permissions, task state, observability, and verification mechanisms.</p>
<p>The model produces decisions and calls. The harness provides the environment in which those decisions can run.</p>
<p>So, when an agent fails, the solution is not always to change the model or write a better prompt. Often, the missing piece is a tool, a context source, a validation step, a clear permission, or a feedback mechanism.</p>
<p>In this article, we use harness to mean the operational layer that connects the agent to the system. In other implementations, it may be spread across configuration files, runtimes, skills, MCP servers, pipelines, and authorization services.</p>
<h2>How do the pieces work together?</h2>
<p>Imagine an agent responsible for investigating a production failure. It loads an investigation skill, checks logs, metrics, traces, and recent deployments, and then organizes the findings into evidence, hypotheses, unknowns, and next steps.</p>
<p>The agent activates the investigation skill. The skill tells it which questions to ask. The tools run the queries. MCP can connect the agent to platforms that provide logs, metrics, traces, or deployment information.</p>
<p>The expected result is not just an answer. It is an investigation with evidence, hypotheses, unknowns, and next steps.</p>
<h2>Creating agents requires limits</h2>
<p>The more capabilities an agent has, the more important it is to control what it can do.</p>
<p>A simple split helps. A read-only agent can inspect information. A proposal agent can suggest a change. An execution agent can apply a change, but it should have stronger controls and usually require approval.</p>
<p>These levels can have different permissions. An agent may read production data without changing resources. It may propose a deployment without running it. It may create a rollback plan without applying it automatically.</p>
<p>The agent design should make these boundaries clear.</p>
<h2>What should last?</h2>
<p>Tools, protocols, and formats may change. The architectural principle remains:</p>
<ul>
<li><p>the agent coordinates a goal;</p>
</li>
<li><p>the skill organizes knowledge and workflow;</p>
</li>
<li><p>the tool runs a capability;</p>
</li>
<li><p>the protocol connects the agent to sources and actions;</p>
</li>
<li><p>permissions limit what can happen.</p>
</li>
</ul>
<p>When a tool changes, we do not need to rewrite all the domain knowledge. We can replace the connection layer and keep the skill, rules, and limits.</p>
<p>This separation reduces the coupling between the context architecture and the tools available at a given time.</p>
<h2>Further reading</h2>
<p>Implementation details vary between platforms. To study the current specifications and formats, check the official sources:</p>
<ul>
<li><p><a href="https://modelcontextprotocol.io/">Model Context Protocol</a></p>
</li>
<li><p><a href="https://opencode.ai/v2/docs/agents">OpenCode Agents</a></p>
</li>
<li><p><a href="https://opencode.ai/v2/docs/skills">OpenCode Skills</a></p>
</li>
<li><p><a href="https://opencode.ai/v2/docs/mcp-servers">OpenCode MCP servers</a></p>
</li>
<li><p><a href="https://context7.com/">Context7</a></p>
</li>
<li><p><a href="https://github.com/hyperf/mcp-incubator">Hyperf MCP incubator</a></p>
</li>
</ul>
<p>These references may change over time. The article remains useful because the separation between agent, knowledge, capability, and connection is broader than any one implementation.</p>
<h2>Conclusion</h2>
<p>Agents, skills, tools, and MCP solve different but connected problems.</p>
<p><strong>The agent coordinates. The skill guides. The tool runs. MCP connects.</strong></p>
<p>When these responsibilities are clear, the architecture becomes easier to change. We can replace a tool without losing domain context. We can create a new skill without building another agent from scratch. We can offer a read-only capability without giving permission to change production.</p>
]]></content:encoded></item><item><title><![CDATA[Context Skills: Breaking Down the Context for AI Agents]]></title><description><![CDATA[This is the sixth article in the series about AI-friendly architecture. We have already discussed how code, documentation, and observability help agents…]]></description><link>https://schons.hashnode.dev/context-skills-breaking-down-the-context-for-ai-agents</link><guid isPermaLink="true">https://schons.hashnode.dev/context-skills-breaking-down-the-context-for-ai-agents</guid><dc:creator><![CDATA[Luiz Schons]]></dc:creator><pubDate>Sat, 05 Sep 2026 14:47:08 GMT</pubDate><content:encoded><![CDATA[<p>This is the sixth article in the series about AI-friendly architecture. We have already discussed how code, documentation, and observability help agents understand systems. We also saw how skills can provide specialized knowledge for specific problems.</p>
<p>Now I want to take a step back and look at the architecture behind these skills.</p>
<p>Just as we learned to split large systems into smaller contexts, we may also need to learn how to split the context we give to agents.</p>
<h2>The Context Monolith</h2>
<p>One of the first attempts to prepare a repository for agents is often to create one central file with all the instructions.</p>
<p>Something like this:</p>
<pre><code class="language-text">AGENTS.md

# How our company works

Architecture
Domains
Coding standards
Deployment processes
Observability
Business rules
Runbooks
Testing conventions
Information about all teams
</code></pre>
<p>At first, this file helps. It creates one place to record decisions and guidance that used to exist only in people's heads.</p>
<p>But it can grow quickly. After some time, it becomes hard to know which parts apply to each task, who is responsible for updating them, and which instructions have priority.</p>
<p>This is a context monolith.</p>
<p>The problem is not having a central file. The problem is putting knowledge from every domain and every tool in the same place without clear boundaries.</p>
<h2>What We Learned from Microservices</h2>
<p>The evolution of microservices taught us an important lesson: large systems are easier to understand when they are split by responsibility and business context.</p>
<p>A bounded context is not only a folder or a separate service. It defines a boundary that contains:</p>
<ul>
<li><p>its own vocabulary;</p>
</li>
<li><p>specific rules;</p>
</li>
<li><p>clear responsibilities;</p>
</li>
<li><p>contracts with other contexts;</p>
</li>
<li><p>related data and behavior;</p>
</li>
<li><p>defined ownership.</p>
</li>
</ul>
<p>For example, inside the payments context, the word transaction may have a specific meaning. Inside the orders context, the same word may appear but represent another part of the business.</p>
<p>Boundaries help prevent every component from needing to know everything.</p>
<p>We can apply a similar idea to the context given to agents.</p>
<h2>Context Can Also Be Decomposed</h2>
<p>Instead of giving every instruction for every problem, we can create specializations:</p>
<img src="https://cdn.hashnode.com/uploads/covers/67081fa628a6c4cdb75a7fc9/c4a20dee-f240-4c5a-9203-1a486b0fed2f.png" alt="One Agent, Several Contexts" style="display:block;margin:0 auto" />

<p>Each skill knows one context deeply. It can have its own vocabulary, rules, tools, and limits.</p>
<p>A payments skill can explain transaction states, refund policies, and the provider integration. A delivery skill can explain the deployment flow, environments, and approvals. An observability skill can guide investigations using logs, metrics, and traces.</p>
<p>The agent still has a general view, but it does not need to carry every detail at the same time.</p>
<h2>Context Skills Are Not Microservices</h2>
<p>It is important to make a distinction.</p>
<p>Context Skills are not the new microservices. A skill is not a production service, does not have a business API by definition, and does not replace a runtime boundary.</p>
<p>The connection between the concepts is the idea of decomposition.</p>
<p>Microservices help separate software capabilities. Context Skills help separate knowledge and workflows for agents.</p>
<p>The same domain may have a service, documentation, dashboards, and one or more related skills. The skill knows the context and knows how to query the right sources, but the data stays in the systems responsible for it.</p>
<img src="https://cdn.hashnode.com/uploads/covers/67081fa628a6c4cdb75a7fc9/2f622eba-b9f9-4eec-874a-51ef1c5c0f3d.png" alt="Distributed Context: Payments" style="display:block;margin:0 auto" />

<p>The value comes from connecting these parts.</p>
<h2>A Skill Is More Than Documentation</h2>
<p>A documentation page explains a subject. A Context Skill also needs to explain how to work with that subject.</p>
<p>It can guide the agent about:</p>
<ul>
<li><p>when that context is relevant;</p>
</li>
<li><p>which sources should be checked first;</p>
</li>
<li><p>which questions need answers;</p>
</li>
<li><p>which evidence should appear in the result;</p>
</li>
<li><p>which actions are allowed;</p>
</li>
<li><p>which actions need approval;</p>
</li>
<li><p>when the investigation should stop.</p>
</li>
</ul>
<p>This creates a work contract for the context. The skill does not need to contain every detail, but it should help the agent find the right information and respect the team's limits.</p>
<p>For example, a payments skill may say that a refund investigation should check the operation contract, emitted events, and failure runbook. It may also require the agent to tell the difference between an authorized transaction and a captured transaction before forming a hypothesis.</p>
<h2>What Does a Context Skill Need to Know?</h2>
<p>A context-based skill does not need to copy all the information in the domain. It needs to know how to navigate it.</p>
<p>Some important elements are:</p>
<h3>Vocabulary</h3>
<p>Which terms have a special meaning in this domain? What is a transaction, an authorization, a capture, or a refund?</p>
<h3>Responsibilities</h3>
<p>Which service is responsible for each action? Who owns the context? Which decisions can the team make?</p>
<h3>Contracts</h3>
<p>Which APIs, events, and schemas connect this context to others? Which changes need versioning?</p>
<h3>Sources of Truth</h3>
<p>Where is the current data? Which documentation should be checked? Which platform contains the production signals?</p>
<h3>Limits</h3>
<p>Which actions are read-only? What needs approval? When should the agent stop and ask for help?</p>
<p>This set of information gives the agent depth without requiring it to carry the knowledge of the whole company.</p>
<p>These limits should be defined together with the architecture's security policies. As we saw in the article <a href="https://luizschons.com/seguran-a-guardrails-e-fitness-functions-para-agentes">Security, Guardrails, and Fitness Functions for Agents</a>, a skill can guide the agent, but it should not be the only protection against an unsafe action. Permissions, scopes, and approvals must also be enforced by the tools and protected resources.</p>
<h2>One Agent Connects the Skills</h2>
<p>Breaking down the context does not mean creating isolated agents that cannot communicate.</p>
<p>The main agent coordinates the specializations.</p>
<p>Imagine an investigation where the payment failure rate has increased. The agent can do the following:</p>
<pre><code class="language-text">1. Activate the Payments Context Skill.
2. Use the Observability Skill to investigate the signals.
3. Use the Delivery Skill to check recent deployments.
4. Use the Data Platform Skill if the flow depends on processed data.
5. Combine the evidence into a hypothesis.
6. Show what was confirmed and what is still uncertain.
</code></pre>
<p>Each skill adds a specific view. The agent connects the information between them.</p>
<p>This separation allows a skill to be maintained by the people who really know that context. They do not need to maintain every instruction for the engineering agent.</p>
<h2>Too Much Context Can Also Be a Problem</h2>
<p>Decomposing does not mean creating hundreds of small skills.</p>
<p>There is a point where too much fragmentation starts to cause problems. If the agent needs to activate ten skills to answer a simple question, the boundaries may be wrong or the task may be split into units that are too small.</p>
<p>A good boundary usually groups knowledge that changes together, is used together, and has related ownership.</p>
<p>Another useful question is: if this context changes, who needs to review the knowledge? If the answer includes very different teams, conflicting responsibilities, or unrelated sources, the boundary may be too large.</p>
<p>Boundaries should also be observable. We need to know which context was consulted, which sources were used, and which rules guided the answer. Without this visibility, it is hard to understand whether a problem came from missing information, the choice of skill, or the agent's interpretation.</p>
<p>We can start with a few larger contexts:</p>
<pre><code class="language-text">Observability
Delivery
Data
Payments
Orders
Security
</code></pre>
<p>Over time, each context can evolve. An observability skill may later gain specializations for incidents, performance, and capacity. This split should happen when there is a real need, not just because it is possible to create more files.</p>
<h2>Where Do Context Skills Live?</h2>
<p>A Context Skill can live with the team's code, in a platform repository, or in a shared catalog. The choice depends on who maintains the context and how the agents will be used.</p>
<p>The most important points are to make clear:</p>
<ul>
<li><p>who owns the skill;</p>
</li>
<li><p>which agents can use it;</p>
</li>
<li><p>which external sources it consults;</p>
</li>
<li><p>how its instructions are versioned;</p>
</li>
<li><p>how changes are reviewed;</p>
</li>
<li><p>which permissions it needs.</p>
</li>
</ul>
<p>For example, a skill for observability may be maintained by the platform team. It can explain how to query standard dashboards and metrics, but it should not store copies of production data.</p>
<p>A payments skill may be maintained by the team responsible for the domain. It can point to ADRs, contracts, and runbooks, but it should continue to respect the original sources.</p>
<h2>Context Skills and Ownership</h2>
<p>Ownership is an essential part of this architecture.</p>
<p>If everyone is responsible for a skill, probably no one is truly responsible. If no one reviews the instructions when an API changes, the agent starts working with outdated knowledge.</p>
<p>A skill can have a simple metadata file:</p>
<pre><code class="language-yaml">name: payments-context
owner: payments-team
review_frequency: quarterly
sources:
  - payment-service
  - payment-documentation
  - payment-dashboards
</code></pre>
<p>This file does not solve the maintenance problem on its own, but it makes responsibility more visible and can help create a review process.</p>
<h2>Conclusion</h2>
<p>For a long time, our concern was decomposing systems so they could be developed and operated by different teams.</p>
<p>Now we also need to decompose the knowledge we give to agents.</p>
<p>Context Skills are a way to create boundaries for this knowledge. They allow each context to have clear vocabulary, rules, tools, ownership, and limits.</p>
<p>The agent does not need to know everything deeply. It needs to know which contexts exist, when to use them, and how to connect the evidence it finds.</p>
<p>In the next article, we will separate the parts that make this architecture work: agents, skills, tools, and MCP. The goal is to understand the role of each layer before using them in a real case.</p>
]]></content:encoded></item><item><title><![CDATA[Guardrails and Fitness Functions for AI-Friendly Architecture]]></title><description><![CDATA[This is the fifth article in the series about AI-friendly architecture. We have already discussed how agents find context, use skills, and work with…]]></description><link>https://schons.hashnode.dev/guardrails-and-fitness-functions-for-ai-friendly-architecture</link><guid isPermaLink="true">https://schons.hashnode.dev/guardrails-and-fitness-functions-for-ai-friendly-architecture</guid><dc:creator><![CDATA[Luiz Schons]]></dc:creator><pubDate>Mon, 31 Aug 2026 01:23:12 GMT</pubDate><content:encoded><![CDATA[<p>This is the fifth article in the series about AI-friendly architecture. We have already discussed how agents find context, use skills, and work with specialized knowledge.</p>
<p>Now we need to answer two questions:</p>
<ul>
<li><p>what can the agent do?</p>
</li>
<li><p>how do we know that the architecture still respects these limits?</p>
</li>
</ul>
<p>Giving context to an agent does not mean giving it unlimited access to the system.</p>
<h2>Instructions Are Not Security Controls</h2>
<p>A common starting point is to add a rule to the prompt or to a skill:</p>
<pre><code class="language-text">Use the production API only for read operations.
Never change data.
Never access admin endpoints.
</code></pre>
<p>This guidance can help direct the agent's behavior, but it should not be treated as a security barrier.</p>
<p>The agent may receive new documents, combine information from different sources, and read the application code. A text that tells the agent to follow a rule may also contain conflicting instructions or show ways to bypass that rule.</p>
<p>The same is true for checks made inside a skill or through an MCP integration. If the same layer that performs the action decides whether it is safe based on context given to the agent, that context may be changed or understood in an unexpected way.</p>
<p>MCP can help connect an agent to systems and tools, but the connection itself is not a security limit. Authorization must exist in the service that protects the resource.</p>
<h2>Even Read Access Can Create a Problem</h2>
<p>Imagine that we want to help an agent investigate an incident. To do this, we give it a token that allows <code>GET</code> requests to the production API.</p>
<p>At first, this looks safe. The agent cannot change orders, restart services, or change settings.</p>
<p>But by reading the code, documentation, and application contracts, the agent may discover other available endpoints. It may find admin routes, internal parameters, connections between resources, or indirect ways to get data that should not be part of the investigation.</p>
<p>The problem is not only the HTTP method used by the request. The problem is the set of resources the agent can reach, the data it receives, and the connections it can discover.</p>
<p>That is why it is not enough to say:</p>
<pre><code class="language-text">GET is allowed.
POST, PUT, and DELETE are not allowed.
</code></pre>
<p>We need to ask:</p>
<ul>
<li><p>which service can be accessed;</p>
</li>
<li><p>which routes are available;</p>
</li>
<li><p>which fields can be returned;</p>
</li>
<li><p>which records can be queried;</p>
</li>
<li><p>which connections between resources can be discovered;</p>
</li>
<li><p>how long the credential is valid;</p>
</li>
<li><p>how each access will be audited.</p>
</li>
</ul>
<h2>How to Provide Context Without Opening Production</h2>
<p>There are safer ways to provide information for an investigation.</p>
<h3>A Limited-Scope Token</h3>
<p>Instead of giving the agent a general API credential, we can create a credential made for the agent:</p>
<pre><code class="language-text">principal: incident-investigation-agent
resource: orders-observability-api
operations:
  - read:incident-summary
  - read:service-health
  - read:dependency-latency
data_policy:
  - exclude:customer.personal_data
  - exclude:payment.card_data
expiration: 15 minutes
</code></pre>
<p>The service that receives this credential must validate its scope. A description in the prompt does not replace this validation.</p>
<h3>Derived Data for Investigation</h3>
<p>We can also keep the agent away from the production application. Events, metrics, sanitized logs, and operational information can be sent to a separate place for queries, such as a replica, a data lake, or a data warehouse.</p>
<img src="https://cdn.hashnode.com/uploads/covers/67081fa628a6c4cdb75a7fc9/7cd5dbfe-0c2e-40c9-88bb-2d7f12fd3cfc.png" alt="Context Without Opening Production" style="display:block;margin:0 auto" />

<p>This layer can apply retention, anonymization, field filtering, and a delay before data is available. The agent receives enough context to investigate without getting a way into the production application.</p>
<h3>The Least Privilege Database Possible</h3>
<p>When direct database access is really needed, it should be created for the task. It should not reuse an application credential:</p>
<pre><code class="language-sql">CREATE ROLE incident_reader LOGIN PASSWORD 'managed-externally';

GRANT CONNECT ON DATABASE operations TO incident_reader;
GRANT USAGE ON SCHEMA incident_data TO incident_reader;
GRANT SELECT ON incident_data.service_health TO incident_reader;
GRANT SELECT ON incident_data.request_summary TO incident_reader;

REVOKE ALL ON SCHEMA billing FROM incident_reader;
REVOKE ALL ON SCHEMA customers FROM incident_reader;
</code></pre>
<p>Besides being read-only, this user should have access only to the tables, views, or schemas needed for the task. Investigation views can hide sensitive data and limit the amount of information returned.</p>
<p>The idea is simple: we should not trust the agent to avoid dangerous tables. The database must block access even if the agent tries to query something outside its scope.</p>
<h2>Guardrails</h2>
<p>Guardrails are mechanisms outside the agent's intentions that validate, limit, or block actions. They can exist in identity, the network, the API, the database, the file system, or the approval process.</p>
<p>A realistic guardrail is not an <code>if</code> statement inside a prompt. It is a policy applied by the service that receives the request.</p>
<p>The policy can check the identity, resource, operation, parameters, requested fields, amount of data, and environment. If any condition is not met, the request is rejected before it reaches the protected resource.</p>
<p>For changes, we can separate preparation from execution.</p>
<p>The agent does not receive the execution credential. Even if it creates a wrong instruction, the execution layer still applies its own rules.</p>
<h3>Example: A CPU Incident</h3>
<p>During an incident, the investigation may show that the pods are running out of CPU. The agent can connect the signals, form a hypothesis, and prepare a change in the infrastructure repository.</p>
<p>It can also open the pull request and create the change request. But the change still depends on a responsible person. That person reviews the impact, approves the change request, and merges or approves the merge. After that, the pipeline applies the change to the pods.</p>
<img src="https://cdn.hashnode.com/uploads/covers/67081fa628a6c4cdb75a7fc9/9ea1e52a-ecc7-4554-8526-5d36780a0363.png" alt="From Incident to Controlled Change" style="display:block;margin:0 auto" />

<p>Guardrails can also limit file paths, network destinations, query size, execution time, number of records, and types of returned data.</p>
<h2>Deterministic Fitness Functions</h2>
<p>Fitness functions come from evolutionary architecture. They turn an important architectural characteristic into an objective and continuous check.</p>
<p>A fitness function does not need AI to decide the result. It can be an architecture test, a pipeline rule, a static analysis, a metadata query, or a periodic check. With the same input state, the result should be reproducible.</p>
<p>The goal is not to judge whether the agent's answer looks good. The goal is to check whether the architecture still has the properties we consider important.</p>
<h3>Example with Deptrac</h3>
<p>One real example is <a href="https://github.com/deptrac/deptrac">Deptrac</a>, a static analysis tool for PHP projects. It lets us define architectural layers and the dependencies allowed between them.</p>
<p>Imagine a system with two contexts: <code>Orders</code> and <code>Billing</code>. An architectural decision may say that <code>Orders</code> must not directly import internal classes from <code>Billing</code>. Communication must happen through a public contract.</p>
<p>This rule can be described in a Deptrac configuration:</p>
<pre><code class="language-yaml">deptrac:
  paths:
    - ./src

  layers:
    - name: Orders
      collectors:
        - type: classLike
          value: .*\\Orders\\.*

    - name: Billing
      collectors:
        - type: classLike
          value: .*\\Billing\\.*

    - name: Shared
      collectors:
        - type: classLike
          value: .*\\Shared\\.*

  ruleset:
    Orders:
      - Shared
    Billing:
      - Shared
</code></pre>
<p>With this rule, a class in <code>Orders</code> that directly imports a class from <code>Billing</code> creates a violation. The pipeline can run:</p>
<pre><code class="language-bash">vendor/bin/deptrac analyse --config-file=deptrac.yaml
</code></pre>
<p>The result is deterministic. Deptrac does not need to understand the agent's intention, judge the quality of the generated code, or interpret a prompt. It only checks the dependencies in the code against the rules defined by the team.</p>
<p>If an agent creates a new class and adds a forbidden dependency, the pull request fails even if the agent was told to respect the limits. The fitness function protects the architectural decision as the system changes.</p>
<h2>Conclusion</h2>
<p>An AI-friendly architecture is not only an architecture that lets the agent do more. It is an architecture that provides context without turning every source of information into a new attack surface.</p>
<p>Prompts, skills, and integrations help guide the agent, but they should not be the last line of defense. Permissions must be enforced by the protected resources, with identity, least privilege, scope, auditing, and the ability to revoke access.</p>
<p>Guardrails limit actions at runtime. Deterministic fitness functions check that these protections remain in place as the architecture changes.</p>
<p>To learn more about fitness functions, read <a href="https://tss-yonder.com/insights/architectural-fitness-functions">Architectural Fitness Functions by Yonder</a> and <a href="https://www.thoughtworks.com/en-us/insights/articles/fitness-function-driven-development">Fitness Function Driven Development by Thoughtworks</a>.</p>
<p>In the next article, we will show how to divide a large block of knowledge into specialized contexts, organized by domain, responsibility, and purpose.</p>
]]></content:encoded></item><item><title><![CDATA[Skills: Specialized Context for AI Agents]]></title><description><![CDATA[This is the fourth article in a series about AI-friendly architecture. We have already talked about context, documentation, and observability. Now I want…]]></description><link>https://schons.hashnode.dev/skills-specialized-context-for-ai-agents</link><guid isPermaLink="true">https://schons.hashnode.dev/skills-specialized-context-for-ai-agents</guid><dc:creator><![CDATA[Luiz Schons]]></dc:creator><pubDate>Fri, 21 Aug 2026 17:22:17 GMT</pubDate><content:encoded><![CDATA[<p>This is the fourth article in a series about AI-friendly architecture. We have already talked about context, documentation, and observability. Now I want to talk about a way to organize knowledge and workflows for agents: skills.</p>
<p>This article is not connected to a specific product or tool. The goal is to discuss the idea of a skill in a general way, independently of the model, agent, or platform used.</p>
<p>The main idea is simple: an agent does not need all possible context. It needs the right context for the problem it is trying to solve.</p>
<h2>The Problem with Putting Everything in One Place</h2>
<p>A common reaction when we start working with agents is to teach them everything at once.</p>
<p>We put company documentation, coding standards, business rules, operational instructions, dashboards, runbooks, and links to many tools in one place.</p>
<p>It seems convenient. After all, the agent has access to more information.</p>
<p>But more information does not always mean more useful knowledge.</p>
<p>When everything is shown at the same time, the agent must figure out:</p>
<ul>
<li><p>which information matters;</p>
</li>
<li><p>which information is reliable;</p>
</li>
<li><p>which pieces are related;</p>
</li>
<li><p>which information should be ignored.</p>
</li>
</ul>
<p>This creates more noise and makes the reasoning harder to follow.</p>
<p>Maybe we are repeating a problem we already had with systems: trying to put too many responsibilities in the same place.</p>
<p>A skill starts from a different idea. Each important context can have its own specialization.</p>
<h2>What Is a Skill?</h2>
<p>A skill is a unit of knowledge and workflow for a specific context.</p>
<p>It can explain:</p>
<ul>
<li><p>which concepts are important in that domain;</p>
</li>
<li><p>which questions should be asked first;</p>
</li>
<li><p>which information sources can be used;</p>
</li>
<li><p>how to understand the data;</p>
</li>
<li><p>which limits and safety rules must be followed;</p>
</li>
<li><p>how to present the result.</p>
</li>
</ul>
<p>For example, an incident investigation skill can guide the agent to identify the affected service, rebuild the timeline, check operational signals, separate facts from guesses, and record the evidence.</p>
<p>It does not need to store every log, metric, or document. It needs to know where to find this information and how to use it.</p>
<p>The value comes from combining knowledge with action.</p>
<h2>Are Skills the New Microservices?</h2>
<p>I would not say that skills are the new microservices. Technically, they are different things.</p>
<p>But there is an important connection between them.</p>
<p>We learned that large systems are easier to change when they are separated by responsibility and business context. Each context can have clearer vocabulary, rules, data, contracts, and ownership.</p>
<p>We can use a similar idea for the context we give to agents.</p>
<p>Instead of creating one agent that knows a little about every domain, we can create specialized contexts with clear boundaries:</p>
<img src="https://cdn.hashnode.com/uploads/covers/67081fa628a6c4cdb75a7fc9/e98f98b4-fb9b-45f6-92fc-df22073ed217.png" alt="Engineering Agent" style="display:block;margin:0 auto" />

<p>The agent still has a general view of the system, but it can use a specialized context when it needs to investigate a specific problem.</p>
<h2>Specialized Context Does Not Mean Isolated Context</h2>
<p>Breaking knowledge into smaller parts does not mean creating boxes that cannot communicate.</p>
<p>An investigation may start with observability, continue through delivery, and end with data. The agent needs to know when each context matters and how to connect the evidence it finds.</p>
<img src="https://cdn.hashnode.com/uploads/covers/67081fa628a6c4cdb75a7fc9/f0a395fc-f30c-4a9d-9f96-547af24fa796.png" alt="Incident Investigation" style="display:block;margin:0 auto" />

<p>A skill provides depth. The agent coordinates the work.</p>
<h2>A Skill Needs Clear Boundaries</h2>
<p>A skill that is too general quickly loses its value.</p>
<p>“Investigate production problems” is a broad instruction. It does not explain where to start, which sources to check, or how to decide that one idea is more likely than another.</p>
<p>A more useful skill defines the context, the goal, the available sources, and the investigation limits:</p>
<pre><code class="language-text">Context:
  APIs and services in the orders domain.

Goal:
  Investigate an increase in latency and identify the
  most likely causes based on evidence.

Sources and capabilities:
  - search logs, metrics, and traces from the orders flow;
  - check the service catalog and its dependencies;
  - check recent changes and deployments;
  - query the database in read-only mode;
  - compare current behavior with an earlier period.

Workflow:
  1. Identify the affected service and endpoint.
  2. Find when the change started.
  3. Compare latency, errors, and request volume.
  4. Check traces and dependencies in the flow.
  5. Check recent changes.
  6. Query order data to test the idea,
     without changing any records.
  7. Record facts, possible causes, and missing information.

Limits:
  - do not change data;
  - do not run write commands;
  - do not expose sensitive data in the result;
  - ask for help when the evidence is not enough.
</code></pre>
<p>This example is more useful because it turns an intention into a process that can be followed.</p>
<p>It also makes one thing clear: querying a database does not mean giving unlimited access. The skill can use a read-only connection with defined tables, fields, and limits for that investigation.</p>
<p>In practice, the agent can access sources through clearly defined capabilities:</p>
<pre><code class="language-text">search_logs(service, time_range)
query_metrics(service, metric, time_range)
get_trace(trace_id)
list_recent_changes(service, time_range)
query_readonly_database(query, parameters)
</code></pre>
<p>Each capability should have its own access policy.</p>
<p>The database query may allow only read operations, limit how long the query can run, and allow access only to the tables needed for that domain.</p>
<p>This does not mean that every skill needs to be rigid. It can allow some flexibility as long as it clearly defines the problem it is meant to solve.</p>
<p>The clearer the context, the less likely the agent is to waste time on irrelevant paths.</p>
<h2>A Skill Also Needs to Know Its Limits</h2>
<p>A good specialist is not only someone who knows what to do. It is also someone who knows when they should not act alone.</p>
<p>A skill should make these points clear:</p>
<ul>
<li><p>which actions are read-only;</p>
</li>
<li><p>which actions need approval;</p>
</li>
<li><p>which data may be exposed;</p>
</li>
<li><p>when there is not enough evidence for a conclusion;</p>
</li>
<li><p>when the problem should be sent to a person.</p>
</li>
</ul>
<p>This is especially important for skills related to production, sensitive data, or changes that may affect other contexts.</p>
<p>Specialized context should not be confused with unlimited autonomy.</p>
<p>These limits cannot exist only as instructions for the agent. We also need fixed guardrails that check permissions, arguments, environment, and impact before any action is executed.</p>
<p>We should never trust that the agent will remember or follow a security rule on its own.</p>
<p>The skill guides the behavior, but the guardrail enforces the restriction. I will explain this difference in more detail later, in an article about guardrails, permissions, and fitness functions.</p>
<h2>The Agent Connects the Contexts</h2>
<p>The biggest value is not only in creating separate skills. It is also in the agent’s ability to connect them.</p>
<p>An observability skill may find unusual behavior. A delivery skill may identify a recent change. A data skill may find a change in a pipeline.</p>
<p>With a broader view, the agent can connect these findings and form a possible explanation:</p>
<blockquote>
<p>The increase in errors started after a recent change. The service began receiving data with a different structure, and the failure is concentrated in orders created after the update.</p>
</blockquote>
<p>This conclusion still needs to be checked, but it is more useful than a list of disconnected facts.</p>
<p>Skills provide depth. The agent provides coordination.</p>
<h2>How to Start Creating Skills</h2>
<p>You do not need to start by creating one skill for every tool or team.</p>
<p>Choose a recurring problem and observe how experienced people solve it today.</p>
<p>Ask:</p>
<ul>
<li><p>which questions are asked first;</p>
</li>
<li><p>which sources are checked;</p>
</li>
<li><p>how the data is understood;</p>
</li>
<li><p>which possible causes are usually rejected;</p>
</li>
<li><p>when the investigation needs to be escalated;</p>
</li>
<li><p>which result format is actually useful.</p>
</li>
</ul>
<p>Then, turn this knowledge into a process that can be found and reused.</p>
<p>A skill does not need to be perfect in its first version. It can improve over time with real cases and include lessons from each investigation.</p>
<h2>Conclusion</h2>
<p>Skills are a way to organize specialized context for agents.</p>
<p>They prevent the agent from receiving a huge amount of information without guidance. They also help make the reasoning more specific, easier to check, and more useful.</p>
<p>The main change is not adding another layer of abstraction. It is recognizing that different problems need different knowledge, sources, and investigation methods.</p>
<p>Just as we learned to separate services by context, we can also separate the knowledge and capabilities of agents by context.</p>
]]></content:encoded></item><item><title><![CDATA[Observability Is Also Context for AI Agents]]></title><description><![CDATA[This is the third article in a series about AI-friendly architecture. We have already talked about how AI is like a new person joining a company and how…]]></description><link>https://schons.hashnode.dev/observability-is-also-context-for-ai-agents</link><guid isPermaLink="true">https://schons.hashnode.dev/observability-is-also-context-for-ai-agents</guid><dc:creator><![CDATA[Luiz Schons]]></dc:creator><pubDate>Fri, 21 Aug 2026 16:42:27 GMT</pubDate><content:encoded><![CDATA[<p>This is the third article in a series about AI-friendly architecture. We have already talked about how AI is like a new person joining a company and how important it is to connect code, documentation, and architectural decisions.</p>
<p>Now I want to talk about a source of context that is often treated only as an operations tool: observability.</p>
<h2>The Real System Is Not Only in the Repository</h2>
<p>When we look at a system through its code, we see a picture of what was built.</p>
<p>But the system running in production may tell a different story.</p>
<p>A configuration may have changed. A dependency may be slower. A certain flow may be used much more than the team expected. A rule may work correctly in most cases but fail with a specific combination of data.</p>
<p>Code shows what should happen. Observability helps us understand what is really happening.</p>
<p>This difference matters to both people and AI agents.</p>
<h2>Observability Is Not Just Monitoring</h2>
<p>Monitoring usually answers a simple question: is something wrong?</p>
<p>Observability tries to answer a bigger question: why is the system behaving this way?</p>
<p>To do this, we use different signals:</p>
<ul>
<li><p>logs show events and details about an execution;</p>
</li>
<li><p>metrics show trends, volumes, and changes;</p>
</li>
<li><p>traces show the path of a request across different services;</p>
</li>
<li><p>deployment events show when a change went to production;</p>
</li>
<li><p>business data shows the impact on people using the system.</p>
</li>
</ul>
<p>Each signal explains one part of the system’s behavior. When these signals are connected, it becomes easier to create a possible explanation and check if it makes sense.</p>
<h2>Where Each Part Lives</h2>
<p>Just as documentation does not need to be inside the code, observability data does not need to be stored in the repository.</p>
<p>The service creates events and signals. An instrumentation layer collects these signals. The observability platform stores and connects the data. Documentation explains the meaning of important metrics, alerts, and flows.</p>
<img src="https://cdn.hashnode.com/uploads/covers/67081fa628a6c4cdb75a7fc9/91445626-d25b-4a7d-a40f-035f366c83ee.png" alt="Observability as context" style="display:block;margin:0 auto" />

<p>The repository can keep only the instrumentation settings, the names of the signals, and links to dashboards and runbooks. The data history stays in the operations platform, which is the right place to check how the system behaves over time.</p>
<p>The important thing is to connect these sources. An agent must be able to leave the service in the repository, reach the right dashboard, and find an explanation of what it is seeing.</p>
<h2>An Incident Is a Context Investigation</h2>
<p>Imagine that an API’s latency starts to increase.</p>
<p>An experienced person may know exactly where to look. They know the right dashboard, remember a similar incident, and know which service usually causes this type of problem.</p>
<p>A new person does not have this knowledge. An agent does not have it either.</p>
<p>To investigate the problem, they need to find a sequence of clues:</p>
<ol>
<li><p>When did the problem start?</p>
</li>
<li><p>Which service showed the first signal?</p>
</li>
<li><p>Did the increase affect all users or only one flow?</p>
</li>
<li><p>Was there a deployment or configuration change during this time?</p>
</li>
<li><p>Which dependency started responding more slowly?</p>
</li>
<li><p>Did the increase in latency affect any business metric?</p>
</li>
</ol>
<p>One dashboard cannot answer all these questions. They require information from different sources.</p>
<p>That is why observability is also context. It gives us evidence about how the system behaves in a specific situation.</p>
<h2>Logs Need to Tell a Story</h2>
<p>A log with a short message may help someone who knows the code. For a larger investigation, it is usually not enough.</p>
<p>Compare these two examples:</p>
<pre><code class="language-text">Error processing payment
</code></pre>
<pre><code class="language-json">{
  "event": "payment_processing_failed",
  "order_id": "ord_123",
  "payment_provider": "provider_a",
  "error_code": "timeout",
  "retry_count": 2,
  "request_id": "req_456",
  "occurred_at": "2026-08-20T18:30:00Z"
}
</code></pre>
<p>The second example is better because it contains information that can be connected to other signals.</p>
<p>With a <code>request_id</code>, we can follow the request across different services. With an <code>order_id</code>, we can understand the impact on one transaction. With the error code, we can group similar failures. With the timestamp, we can compare the event with deployments and infrastructure changes.</p>
<p>Structured logs make the system’s behavior easier to understand.</p>
<h2>Metrics Need to Have Meaning</h2>
<p>It is possible to have many dashboards and still have poor observability.</p>
<p>A metric is useful only when we know what it means, what behavior it describes, and when we should pay attention to it.</p>
<p>For example, a metric called <code>request_count</code> may show the number of requests. But we also need to know:</p>
<ul>
<li><p>what the time unit is;</p>
</li>
<li><p>which service creates this metric;</p>
</li>
<li><p>which filters are available;</p>
</li>
<li><p>what change is considered normal;</p>
</li>
<li><p>how it relates to errors and latency.</p>
</li>
</ul>
<p>Without this context, an agent may find the right metric and still understand it incorrectly.</p>
<p>A good practice is to document the most important metrics with their meaning, dimensions, and known limits. The dashboard becomes a visual explanation of the system.</p>
<h2>Traces Connect Architecture to Behavior</h2>
<p>In a distributed architecture, a request may pass through several services before reaching the user.</p>
<p>When we look at each service separately, we lose part of the story. A trace lets us follow the full path and see where time was spent.</p>
<p>This helps answer questions such as:</p>
<ul>
<li><p>which service added the most latency;</p>
</li>
<li><p>which call was repeated several times;</p>
</li>
<li><p>where the first error happened;</p>
</li>
<li><p>which external dependency is affecting the flow;</p>
</li>
<li><p>whether a failure in one service is causing problems in others.</p>
</li>
</ul>
<p>For an agent, traces connect the architecture map to a real execution. They show how the components worked together, not only how they were described in a document.</p>
<h2>Operational Context Must Include the Business</h2>
<p>One common mistake in observability is looking only at the technical health of the system.</p>
<p>CPU, memory, error rate, and latency are important. But they do not always show the real impact on the people using the product.</p>
<p>An API may return a successful response while an important business step is failing. A flow may have few errors but affect the most important customers. A queue may be processing messages but with a delay that creates a bad user experience.</p>
<p>Because of this, it is useful to connect technical signals with business events:</p>
<ul>
<li><p>approved payments;</p>
</li>
<li><p>completed orders;</p>
</li>
<li><p>processed documents;</p>
</li>
<li><p>users who completed a step;</p>
</li>
<li><p>transactions that needed manual help.</p>
</li>
</ul>
<p>This context helps the agent focus on what really matters. Not every technical alert is a business incident, and not every business problem appears as an obvious technical error.</p>
<h2>What Happens After a Deployment?</h2>
<p>A code change can only be properly evaluated after it starts running.</p>
<p>The context of a change should also include its connection to production signals. When a deployment happens, we should be able to answer:</p>
<ul>
<li><p>which services changed;</p>
</li>
<li><p>which metrics need to be watched;</p>
</li>
<li><p>what behavior we expect;</p>
</li>
<li><p>which alerts may show a problem;</p>
</li>
<li><p>how to compare the old behavior with the new behavior.</p>
</li>
</ul>
<p>With these connections, an agent can help not only write a change but also check whether it produced the expected result.</p>
<h2>How to Prepare Observability for Agents</h2>
<p>You do not need to instrument everything at once. It is better to choose one important flow and make it easy to understand from start to finish.</p>
<p>These steps can help:</p>
<ol>
<li><p>Choose a flow that matters to the business.</p>
</li>
<li><p>Make sure it has a correlation ID.</p>
</li>
<li><p>Structure the main logs for this flow.</p>
</li>
<li><p>Create metrics with clear names and meanings.</p>
</li>
<li><p>Check that traces pass through the services involved.</p>
</li>
<li><p>Connect deployments, incidents, and configuration changes.</p>
</li>
<li><p>Document what is normal and what shows unusual behavior.</p>
</li>
</ol>
<p>The goal is not only to create better-looking dashboards. The goal is to build a system that can tell its own story when something changes.</p>
<h2>Conclusion</h2>
<p>Observability is one of the most important ways to give context to AI agents.</p>
<p>Code and documentation explain how the system was designed. Logs, metrics, and traces show how it behaves when people are really using it.</p>
<p>When these signals are structured, connected, and linked to business context, a new person or an AI agent can investigate problems with less help from people who already know the system.</p>
<p>In the next article, I will talk about skills. The idea is to understand how to give an agent the right context for each problem, with specialized skills for observability, delivery, data, and other technical areas.</p>
]]></content:encoded></item><item><title><![CDATA[Code Is Not Context: Designing a Context Architecture for Agents]]></title><description><![CDATA[This is the second article in a series about AI-friendly architecture. In the first article, I explained how AI is like a new person joining a company and…]]></description><link>https://schons.hashnode.dev/code-is-not-context-designing-a-context-architecture-for-agents</link><guid isPermaLink="true">https://schons.hashnode.dev/code-is-not-context-designing-a-context-architecture-for-agents</guid><dc:creator><![CDATA[Luiz Schons]]></dc:creator><pubDate>Fri, 21 Aug 2026 02:08:37 GMT</pubDate><content:encoded><![CDATA[<p>This is the second article in a series about AI-friendly architecture. In the first article, I explained how AI is like a new person joining a company and trying to understand a system for the first time.</p>
<p>Now I want to explore an important part of this comparison: where does this person find the information they need?</p>
<h2>Code Is Only Part of the Story</h2>
<p>When someone new joins a team, they usually start with the repository. That is where the services, endpoints, models, tests, and business rules are.</p>
<p>But code rarely answers everything.</p>
<p>It may show that a decision exists, but not explain why it was made. It may show that two services communicate, but not make it clear which service owns that responsibility. It may show a business rule, but not explain when it applies or what happens when it changes.</p>
<p>To truly understand a system, a person needs to combine several sources:</p>
<ul>
<li><p>code;</p>
</li>
<li><p>documentation;</p>
</li>
<li><p>architectural decisions;</p>
</li>
<li><p>tickets and epics;</p>
</li>
<li><p>previous incidents;</p>
</li>
<li><p>dashboards and metrics;</p>
</li>
<li><p>conversations with team members.</p>
</li>
</ul>
<p>The same is true for AI agents.</p>
<p>The problem is that, in many companies, this information exists but is not connected. The code is in a repository. The decisions are in a documentation tool. The tickets are in another system. The incidents are in an operations platform. And the most important context is still in the minds of a few people.</p>
<p>Having information is not the same as having context.</p>
<h2>What Is a Context Architecture?</h2>
<p>I like to think of Context Architecture as a way to organize and connect the different sources of knowledge that explain a system.</p>
<p>It is not about creating one huge document with everything the company knows. It is also not about copying all the documentation into a vector database and waiting for the answer to appear.</p>
<p>It is about creating paths that help a person or an agent answer questions such as:</p>
<ul>
<li><p>What is this service responsible for?</p>
</li>
<li><p>Which other systems depend on it?</p>
</li>
<li><p>Why was it built this way?</p>
</li>
<li><p>Which business rule is being used here?</p>
</li>
<li><p>How do we know that it is working correctly?</p>
</li>
<li><p>What happened the last time this part changed?</p>
</li>
</ul>
<p>A well-designed context architecture does not remove the need for reasoning. It reduces the effort needed to find the right information.</p>
<h2>Context Does Not Need to Be in One Place</h2>
<p>We may want to put everything in one place. We could create a big page called “How the Company Works” and expect everyone to find what they need there.</p>
<p>In practice, this document becomes outdated very quickly. It becomes hard to know what is still valid, who should update it, and which part applies to each situation.</p>
<p>Maybe it is better to think of context as a network:</p>
<img src="https://cdn.hashnode.com/uploads/covers/67081fa628a6c4cdb75a7fc9/9fb6215a-d1db-49a0-bd1b-59c5db990aab.png" alt="Context Architecture" style="display:block;margin:0 auto" />

<p>Each source can stay where it works best. What changes is that there are clear links between them.</p>
<p>A task should point to the affected domain. The domain should point to the services involved. The service should have its operational documentation. Important decisions should be recorded. Production signals should also be easy to find.</p>
<p>The goal is not to centralize knowledge. The goal is to make it easy to find.</p>
<h2>Example Structure</h2>
<p>Let’s imagine a payments domain. The code may be in a repository, while the documentation, ADRs, and runbooks stay in the tools the team already uses:</p>
<img src="https://cdn.hashnode.com/uploads/covers/67081fa628a6c4cdb75a7fc9/2b3eab9e-984a-4bb7-94c2-3ade8f135b7f.png" alt="Distributed context: payments" style="display:block;margin:0 auto" />

<p>The repository’s <code>README.md</code> can explain the service’s responsibility and link to the <code>Architecture Hub</code>. The <code>context.md</code> page can describe business concepts and domain boundaries. ADRs can stay in an architecture tool, runbooks can stay in a documentation platform, and dashboards can stay in the observability tool.</p>
<p>The <code>links.md</code> file does not need to copy the content from these sources. It can work as a trusted index, with links to where each piece of information is stored.</p>
<p>An agent that receives a payments task does not need to read every file in the repository right away. It can start with the domain map, follow the links related to the task, and check each source at the right time.</p>
<p>The most important part of this structure is not putting everything in the same folder. It is making clear where each type of knowledge lives and how to reach it.</p>
<h2>Context Needs an Owner</h2>
<p>Documentation without an owner usually becomes abandoned documentation.</p>
<p>If no one knows who should update a page, the page will become outdated. If an architectural decision does not say who made it and why, it may later look like a rule with no reason.</p>
<p>That is why a Context Architecture also needs to answer:</p>
<ul>
<li><p>Who is responsible for this context?</p>
</li>
<li><p>When was it last updated?</p>
</li>
<li><p>Which system is the original source of this information?</p>
</li>
<li><p>How do we know that it is still valid?</p>
</li>
</ul>
<p>People and AI agents both need this. An agent may find a useful page, but it still needs to check if the information is up to date, belongs to the right system, and is useful for making a decision.</p>
<p>Context without a clear validity signal can be as dangerous as having no context at all.</p>
<h2>The Role of Architectural Decisions</h2>
<p>One of the most valuable sources of information about a system is the history of its decisions.</p>
<p>The current code shows the result of many choices. An ADR helps explain those choices.</p>
<p>It can record:</p>
<ul>
<li><p>the problem that needed to be solved;</p>
</li>
<li><p>the options that were considered;</p>
</li>
<li><p>the criteria used to make the decision;</p>
</li>
<li><p>the trade-offs that were accepted;</p>
</li>
<li><p>the expected results.</p>
</li>
</ul>
<p>Without this history, someone may look at an implementation and think that it could be much simpler. Maybe it could. But there may have been a constraint that is no longer visible in the code.</p>
<p>For an agent, this difference is very important. Without the context behind a decision, it may suggest a technically elegant change that does not fit a real business need or a known operational limit.</p>
<h2>Context Should Follow the Workflow</h2>
<p>Another important point is that context should not be treated as a separate activity from development.</p>
<p>If documentation is updated only during one big review each year, it will fall behind. If ADRs are written only when someone remembers to write them, many important decisions will disappear. If incidents do not leave written lessons, the team will investigate the same problems again and again.</p>
<p>Context needs to be part of the normal workflow:</p>
<ol>
<li><p>A change starts with a task or an epic.</p>
</li>
<li><p>The team identifies the affected domains and services.</p>
</li>
<li><p>The important decisions are recorded.</p>
</li>
<li><p>The documentation is updated together with the code.</p>
</li>
<li><p>Observability shows what happens after the deployment.</p>
</li>
<li><p>Incidents and lessons learned are added to the knowledge base.</p>
</li>
</ol>
<p>This does not need to be a heavy process. The important thing is to create and maintain these links close to the moment when the knowledge is created.</p>
<h2>What Should a New Person Be Able to Do?</h2>
<p>A good way to test this architecture is to choose a real task and imagine a new person trying to complete it.</p>
<p>Would this person be able to find out:</p>
<ul>
<li><p>which part of the system needs to change;</p>
</li>
<li><p>who owns the domain;</p>
</li>
<li><p>which decisions limit the solution;</p>
</li>
<li><p>how to check that the change worked;</p>
</li>
<li><p>where to investigate if something goes wrong?</p>
</li>
</ul>
<p>If the answer to all these questions is “you need to talk to someone,” there is an opportunity to improve the system’s context.</p>
<p>The same test can be used with an AI agent. The difference is that an agent will make it even clearer when important information depends on informal knowledge.</p>
<p>A simple way to measure this is to choose a small task and watch the steps needed to solve it. If someone needs to open several unrelated systems or ask another person before every decision, the problem is not only documentation. It is a context architecture problem.</p>
<h2>Context Architecture Is Not Bureaucracy</h2>
<p>It is possible to turn this topic into another set of required processes. I do not think that is the goal.</p>
<p>The goal is not to write documentation just for the sake of writing it. The goal is to reduce the time people spend looking for answers and reduce their dependence on the people who were present when a decision was made.</p>
<p>A well-maintained context architecture helps with onboarding, incident investigation, feature planning, and system maintenance. AI agents simply make this need easier to see.</p>
<p>In the end, this is an old practice facing a new challenge: making system knowledge more explicit so that it does not stay in the memory of only a few people.</p>
<h2>Conclusion</h2>
<p>Code explains how the system works at a specific point in time. Context helps explain why it works that way, what limits exist, and how it connects to the rest of the organization.</p>
<p>A Context Architecture does not need to put everything in one place. It needs to connect the right sources, make clear who owns each context, and make information easy to find.</p>
<p>When we do this, the system becomes easier to understand for people joining the team, for people investigating a problem, and for the agents working with us.</p>
<p>In the next article, I will talk about observability as a source of context. Logs, metrics, and traces are not only used to find errors. They also help explain how the system really behaves.</p>
]]></content:encoded></item><item><title><![CDATA[Is your software ready to be understood by an AI?]]></title><description><![CDATA[This is the first article in a series about AI-friendly architecture, a way of thinking about systems, context, and tools so that AI agents can work more…]]></description><link>https://schons.hashnode.dev/seu-software-est-pronto-para-ser-entendido-por-uma-ia</link><guid isPermaLink="true">https://schons.hashnode.dev/seu-software-est-pronto-para-ser-entendido-por-uma-ia</guid><dc:creator><![CDATA[Luiz Schons]]></dc:creator><pubDate>Thu, 20 Aug 2026 23:24:14 GMT</pubDate><content:encoded><![CDATA[<p>This is the first article in a series about AI-friendly architecture, a way of thinking about systems, context, and tools so that AI agents can work more effectively with real-world software.</p>
<p>Maybe the best way to think about an AI working in your system is to imagine a new person entering the company.</p>
<p>They don’t know the domain yet, they don’t know which services exist, they don’t understand past decisions, and they don’t know where to find the answers. To start contributing, they need to go a onboarding process.</p>
<p>This person may try to figure things out. They may also ask someone more experienced. But when knowledge is well documented, they can find the answers on their own and move forward with greater autonomy.</p>
<p>Something similar happens with AI agents. The more an agent depends on a specific person, informal context, or the memory of someone who already knows how to perform a task, the worse it performs.</p>
<p>The goal of an AI-friendly architecture is to make this knowledge more explicit, accessible, and easier to navigate. Not only for AI, but also for anyone new joining the team.</p>
<p>Over the past few years, the way we build software has changed considerably. First, we learned how to scale systems. Then, we learned how to modularize code. Later, we started separating responsibilities, distributing services, and organizing teams around context. Now we are entering a new phase: creating software that is not only used by people, but can also be understood by AI agents.</p>
<p>This may seem subtle at first, but it changes quite a lot. An agent does not understand your system in the same way an experienced developer does. It needs signals, context, structure, and paths for discovery. If that context is not well organized, AI can still help, but it will work with more noise, more trial and error, and less precision.</p>
<h2>The problem is not a lack of intelligence</h2>
<p>When an AI makes a mistake while analyzing a system, the most common explanation is usually: “the model is not good enough.” Sometimes that is true. But many failures happen for another reason: the system was designed for people who already know the context, not for agents that need to discover it.</p>
<p>An in-house developer knows:</p>
<ul>
<li><p>where the relevant documentation is;</p>
</li>
<li><p>which decisions were recorded in old ADRs;</p>
</li>
<li><p>which service owns each responsibility;</p>
</li>
<li><p>where to look in the event of an incident;</p>
</li>
<li><p>which metrics indicate that something is actually wrong.</p>
</li>
</ul>
<p>By default, an agent does not know any of this. It can figure out some things on its own, but it still needs clear context. When context is scattered, hidden, or inconsistent, the quality of the response declines as well.</p>
<h2>Code Is Not Enough Context</h2>
<p>There is a common trap: assuming that because AI can read code, it already understands the system.</p>
<p>But code is only part of the story.</p>
<p>Code shows the implementation. It does not necessarily show:</p>
<ul>
<li><p>why that decision was made;</p>
</li>
<li><p>which business problem it solves;</p>
</li>
<li><p>which trade-offs were accepted;</p>
</li>
<li><p>which parts are sensitive;</p>
</li>
<li><p>what has already been tried and discarded;</p>
</li>
<li><p>how the system behaves in production.</p>
</li>
</ul>
<p>In practice, a mature system exists in more places than the repository. It lives in documentation, observability, incidents, tickets, architectural decisions, dashboards, and the team’s collective memory. The challenge now is to transform this knowledge into something an AI can navigate.</p>
<h2><strong>Architecture That Agents Can Understand</strong></h2>
<p>If I had to summarize the central idea of this article in one sentence, it would be this:</p>
<blockquote>
<p>AI-friendly architecture is the discipline of organizing systems so that agents can discover, correlate, and apply context with minimal friction.</p>
</blockquote>
<p>This does not mean exposing everything to AI. On the contrary, it means making context:</p>
<ul>
<li><p>more accessible;</p>
</li>
<li><p>more structured;</p>
</li>
<li><p>more reliable;</p>
</li>
<li><p>more connected;</p>
</li>
<li><p>more verifiable.</p>
</li>
</ul>
<p>In other words, having information is not enough. Information needs to be discoverable and combinable.</p>
<h3>What This Changes in Practice</h3>
<p>An AI-friendly system tends to have a few characteristics:</p>
<ol>
<li><p>**Living documentation<br />**Documentation cannot be a graveyard of outdated pages. It needs to be part of the system and keep up with relevant changes.</p>
</li>
<li><p><strong>Recorded decisions</strong><br />Architecture without history becomes guesswork. ADRs, RFCs, and decision notes help the agent understand the “why,” not just the “how.”</p>
</li>
<li><p><strong>Observability as a source of truth</strong><br />Logs, traces, and metrics are not only used to operate the system. They also help explain its behavior.</p>
</li>
<li><p><strong>Context organized by domain</strong><br />Not all knowledge needs to live in the same place. Each context can have its own documentation, rules, and reference points.</p>
</li>
<li><p><strong>Tools connected to the workflow</strong><br />The agent needs access to systems that actually tell the story of the software: observability tools, backlogs, repositories, incidents, and deployments.</p>
</li>
</ol>
<h2>The Old Way of Organizing Software</h2>
<p>This conversation reminds me a lot of the evolution of microservices.</p>
<p>In the past, we tried to put everything into large and centralized systems. Over time, we learned that it made more sense to separate things by domain, responsibility, and context. Not because splitting things up is elegant, but because smaller systems with clear boundaries are easier to understand and evolve.</p>
<p>Now the question is similar:</p>
<blockquote>
<p>If we have learned to decompose systems by context, why do we continue delivering context to AI in massive blocks?</p>
</blockquote>
<p>Maybe the next evolution is this: not only decomposing services, but also decomposing the knowledge that powers agents.</p>
<h2>The Role of Skills</h2>
<p>This is where an important piece comes in: skills.</p>
<p>A skill is a way of packaging specialized context. Instead of giving the agent an ocean of generic information, you provide clear, well-bounded blocks of knowledge.</p>
<p>Think of examples such as:</p>
<ul>
<li><p>a skill for Datadog;</p>
</li>
<li><p>a skill for Argo;</p>
</li>
<li><p>a skill for Databricks;</p>
</li>
<li><p>a skill for New Relic;</p>
</li>
<li><p>a skill for incident investigation;</p>
</li>
<li><p>a skill for performance analysis;</p>
</li>
<li><p>a skill for production changes.</p>
</li>
</ul>
<p>The idea is not to make the agent know everything at the same time. It is to allow it to activate the right context at the right time.</p>
<p>This reduces noise and increases precision. Instead of trying to read everithing the agent works with context that is more specific and focused on the problem.</p>
<h2>A Simple Example</h2>
<p>Imagine that an incident has started affecting an API’s latency. Without an AI-friendly architecture, the agent may still try to help, but it will depend heavily on luck:</p>
<ul>
<li><p>looking for logs in the wrong places;</p>
</li>
<li><p>interpreting dashboards without knowing which metrics matter;</p>
</li>
<li><p>wasting time on irrelevant paths;</p>
</li>
<li><p>suggesting generic hypotheses.</p>
</li>
</ul>
<p>With better-organized context, the workflow changes:</p>
<ol>
<li><p>The agent identifies the affected service.</p>
</li>
<li><p>It consults the domain documentation.</p>
</li>
<li><p>It correlates observability signals.</p>
</li>
<li><p>It finds the related architectural decisions.</p>
</li>
<li><p>It focuses the investigation on the most likely hypotheses.</p>
</li>
</ol>
<p>The benefit here is not that AI performs magic. It is that the system is prepared for AI to work with less friction and greater precision.</p>
<h2>What Is Worth Starting Now</h2>
<p>If you want to get started in a practical way, I would suggest four areas of focus:</p>
<ul>
<li><p>review the system’s most important documentation;</p>
</li>
<li><p>record architectural decisions more consistently;</p>
</li>
<li><p>improve operational clarity through observability;</p>
</li>
<li><p>separate contexts that are currently too mixed together.</p>
</li>
</ul>
<p>You do not need to transform everything at once. In fact, the best approach may be to start with the areas where the team struggles most: incidents, onboarding, and changes in critical areas.</p>
<h2>Conclusion</h2>
<p>Software that is ready to be understood by AI is not software “built for robots.” It is software with context that is clearer, more accessible, and more useful to anyone or anything that needs to understand the system quickly.</p>
<p>At its core, the idea is simple: if architecture helps people make better decisions, it can also help agents make better decisions.</p>
<p>And maybe this is the next major evolution of software architecture: not just systems that scale, but systems that can be quickly understood by humans and agents working together.</p>
<p>This was only the beginning. In the next article, I will discuss why code is not enough context and how to design a Context Architecture for agents by connecting code, documentation, architectural decisions, and business knowledge.</p>
]]></content:encoded></item><item><title><![CDATA[Microservices são débitos técnicos?]]></title><description><![CDATA[Desenvolver software nunca esteve tão complexo e ao mesmo tempo tão simples. A complexidade está na quantidade de tecnologias, frameworks, linguagens e…]]></description><link>https://schons.hashnode.dev/microservices-sao-debitos-tecnicos</link><guid isPermaLink="true">https://schons.hashnode.dev/microservices-sao-debitos-tecnicos</guid><dc:creator><![CDATA[Luiz Schons]]></dc:creator><pubDate>Fri, 06 Dec 2024 00:57:36 GMT</pubDate><content:encoded><![CDATA[<p>Desenvolver software nunca esteve tão complexo e ao mesmo tempo tão simples. A complexidade está na quantidade de tecnologias, frameworks, linguagens e padrões que temos disponíveis para desenvolver software. A simplicidade está na facilidade de acesso a essas tecnologias e na facilidade de desenvolver software.</p>
<p>Com a facilidade de desenvolver software, muitas vezes, não nos preocupamos com a qualidade do software que estamos desenvolvendo. A qualidade do software é um fator muito importante para o sucesso de um projeto. É ela que garante que o software seja fácil de manter, fácil de evoluir e fácil de corrigir.</p>
<p>Muitas vezes focamos os nossos estudos e nosso tempo para o aprendizado de novas tecnologias e frameworks, mas esquecemos de estudar e aprender sobre arquitetura de software e isso é um grande erro. A arquitetura de software é o que garante a a manutenibilidade, a evolução e a correção do software.</p>
<p>Decidir qual arquitetura utilizar em um projeto é uma tarefa muito importante e que deve ser feita com muito cuidado. Uma escolha não muito acertada pode trazer muitos problemas para o projeto e para a equipe de desenvolvimento, e grande problema é que esses efeitos colaterais podem não ser percebidos no início do projeto, mas sim ao longo do tempo.</p>
<p>Alguns dos principais problemas que podem ser causados por uma arquitetura de software não muito acertada são grande acoplamento entre os módulos do software, baixa coesão entre os módulos, dificuldade em escalar, problemas de performance, dificuldade em testar entre outros.</p>
<p>Imagine o seguinte cenário: o tempo foi passando, você e sua equipe foram notando que o software está ficando cada vez mais complexo, parece que a cada nova funcionalidade que é implementada, o software fica mais instável, mais difícil de manter, mais difícil de evoluir e mais difícil de corrigir. E agora? O que fazer?</p>
<p>Eis que surge do nada a palavra mágica: <strong>Microservices</strong>. Microservices é a solução para todos os problemas de arquitetura de software. Microservices é a solução para todos os problemas de acoplamento, coesão, escalabilidade, performance e testes.</p>
<p>Eu concordo que micro-serviços são uma excelente solução para muitos problemas de arquitetura de software, mas não são a solução para todos os problemas.</p>
<p>Você já pensou em refatorar seu código antes de tentar uma solução mais complexa como micro-serviços?</p>
<p>Pensou em estudar e aprender sobre arquitetura de software antes de tentar uma solução baseada puramente em tendências do momento ou em soluções que funcionaram para outras empresas?</p>
<p>A <code>Netflix</code>, <code>Amazon</code>, <code>Google</code> tem soluções ótimas para os problemas de arquitetura de software dela, mas será que essas soluções funcionam para o seu projeto?</p>
<p>Será que está na hora de você e sua equipe pararem de tentar soluções mágicas e começarem a estudar e aprender sobre qualidade e arquitetura de software?</p>
<p>Você não acha que usar micro-serviços para resolver problemas de arquitetura de software é um débito técnico?</p>
<p>Não vou dar resposta para essas perguntas, mas acho que você deveria pensar sobre isso.</p>
<p>Não acho que você nunca deve usar micro-serviços, mas acho que você deve pensar muito antes de usar.</p>
<p>Existem sim casos onde essa arquitetura faz todo sentido, mas existe uma grande quantidade de casos onde essa arquitetura só esta sendo usada para resolver débitos técnicos deixados por uma decisão errada tomada no início do projeto.</p>
<p>Não abomine microservices, mas não os use como solução para todos os problemas.</p>
<p>Estude, aprenda, e tome decisões baseadas em fatos e não em tendências.</p>
<p>Manter um software de qualidade é um trabalho árduo e que exige muito estudo e dedicação, mas é um trabalho que vale a pena.</p>
<p>E você, o que acha? Microservices são débitos técnicos?</p>
]]></content:encoded></item><item><title><![CDATA[Acoplamento estático e dinâmico em Microservices]]></title><description><![CDATA[Aplicações monolíticas são aquelas que possuem um único código fonte, um único executável e um único processo. Muitas vezes, essas aplicações são…]]></description><link>https://schons.hashnode.dev/acoplamento-estatico-e-dinamico-em-microservices</link><guid isPermaLink="true">https://schons.hashnode.dev/acoplamento-estatico-e-dinamico-em-microservices</guid><category><![CDATA[Microservices]]></category><category><![CDATA[Developer]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[distributed system]]></category><dc:creator><![CDATA[Luiz Schons]]></dc:creator><pubDate>Fri, 11 Oct 2024 20:02:39 GMT</pubDate><content:encoded><![CDATA[<p>Aplicações monolíticas são aquelas que possuem um único código fonte, um único executável e um único processo. Muitas vezes, essas aplicações são construídas em uma única linguagem de programação e são implantadas em um único servidor.</p>
<p>Quando começamos a desenvolver, monolitos são a nossa primeira experiência na maioria das vezes. Eles são fáceis de desenvolver, testar e implantar. No entanto, à medida que a aplicação cresce, o monolito começa a se tornar um problema.</p>
<p>A escalabilidade de um software é a uma das coisas mais importantes a serem consideradas hoje. Saber lidar de forma dinâmica com o crescimento de usuários e dados é essencial para o sucesso de um software.</p>
<p>Sistemas monolíticos são difíceis de escalar. Eles são construídos como um único bloco de código, o que significa que, para escalar a aplicação, você precisa replicar todo o código ou escalar verticalmente o servidor. Isso pode ser caro e ineficiente.</p>
<blockquote>
<p><strong>Escalabilidade vertical</strong> é a capacidade de aumentar a capacidade de um servidor, adicionando mais recursos, como CPU, RAM e disco. Isso é feito para melhorar o desempenho de um servidor.</p>
<p><strong>Escalabilidade horizontal</strong> é a capacidade de aumentar a capacidade de um sistema, adicionando mais instâncias do sistema. Isso é feito para melhorar a disponibilidade e a confiabilidade de um sistema.</p>
</blockquote>
<p>Microservices é uma arquitetura de software que resolve esse problema (mas cria outros). Em vez de construir uma única aplicação monolítica, você constrói várias aplicações pequenas e independentes, chamadas de micro-serviços. Cada microservice é responsável por uma parte específica da aplicação e se comunica com outras aplicações.</p>
<p>Embora, em teoria, os micro-serviços pareçam oferecer uma solução ideal para problemas de escalabilidade, eles apresentam desafios próprios na prática. Um dos principais desafios é o gerenciamento do acoplamento entre eles.</p>
<p>No mundo ideal, microservices devem ser independentes e desacoplados. Isso significa que uma aplicação deve ser capaz de funcionar sem depender de outra. No entanto, principalmente em migrações de sistemas monolíticos para microservices, é comum que dependam uns dos outros e chamamos isso de acoplamento.</p>
<p>Existem dois tipos de acoplamento em microservices: acoplamento estático e acoplamento dinâmico, e é sobre eles que vamos falar hoje.</p>
<hr />
<h2><strong>Exemplo de arquitetura de microservices</strong></h2>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728676759014/bb9c5c1b-8245-4a17-9691-a5a166d7539f.png" alt="" style="display:block;margin:0 auto" />

<h3><strong>Componentes:</strong></h3>
<ol>
<li><p><strong>API Gateway</strong>:</p>
<ul>
<li><p>Atua como o ponto de entrada para todas as solicitações externas ao sistema.</p>
</li>
<li><p>Recebe requisições HTTP dos clientes e as encaminha para os serviços internos apropriados (como ProductCatalogService, OrderService, e UserService).</p>
</li>
<li><p>Manter o controle de acesso, autenticação, roteamento de requisições, e agregação de respostas.</p>
</li>
</ul>
</li>
<li><p><strong>ProductCatalogService</strong>:</p>
<ul>
<li><p>Gerencia informações sobre produtos à venda, como descrições, preços e disponibilidade.</p>
</li>
<li><p>Pode consultar um serviço de descoberta como o Consul para localizar outros serviços necessários.</p>
</li>
</ul>
</li>
<li><p><strong>OrderService</strong>:</p>
<ul>
<li><p>Gera pedidos de compra.</p>
</li>
<li><p>Interage com outros serviços para obter informações do produto (via ProductCatalogService) e detalhes do usuário (via UserService).</p>
</li>
<li><p>Manipula pedidos no banco de dados MongoDB e publica eventos de pedidos para o RabbitMQ, permitindo comunicação assíncrona com outros serviços.</p>
</li>
</ul>
</li>
<li><p><strong>UserService</strong>:</p>
<ul>
<li><p>Administra informações de usuários, como perfis, preferências e histórico de compras.</p>
</li>
<li><p>Permite que outros serviços venham a consultar informações do usuário de forma segura.</p>
</li>
</ul>
</li>
<li><p><strong>PaymentService</strong>:</p>
<ul>
<li><p>Responsável pelo processamento de transações de pagamento.</p>
</li>
<li><p>Interage com o banco de dados MongoDB para armazenar informações sobre transações financeiras.</p>
</li>
<li><p>Publica eventos de transação no RabbitMQ para permitir que outros serviços (como o NotificationService) sejam notificados sobre ocorrências financeiras.</p>
</li>
</ul>
</li>
<li><p><strong>RabbitMQ</strong>:</p>
<ul>
<li><p>Permite a mensageria assíncrona entre serviços, facilitando a comunicação desacoplada.</p>
</li>
<li><p>Transmite eventos de pedidos e transações para partes interessadas.</p>
</li>
</ul>
</li>
<li><p><strong>NotificationService</strong>:</p>
<ul>
<li>Envia notificações para os usuários com base em eventos recebidos através do RabbitMQ, como atualizações de status de pedidos ou mensagens de confirmação de pagamento.</li>
</ul>
</li>
<li><p><strong>MongoDB</strong>:</p>
<ul>
<li><p>Armazena dados críticos para o sistema, como informações de pedidos, usuários e transações.</p>
</li>
<li><p>Um banco de dados NoSQL, como o MongoDB, é bem adequado para sistemas que exigem flexibilidade de esquema e manuseio eficiente de dados não estruturados.</p>
</li>
</ul>
</li>
<li><p><strong>Consul</strong>:</p>
<ul>
<li><p>Serviço utilizado para descoberta, configuração e gerenciamento de serviços.</p>
</li>
<li><p>Permite que microservices descubram dinamicamente a localização de outros serviços, facilitando uma arquitetura altamente flexível e resiliente.</p>
</li>
</ul>
</li>
<li><p><strong>Nginx</strong>:</p>
<ul>
<li>Pode representar um servidor web reverso ou balançador de carga que distribui requisições entre várias instâncias do API Gateway, melhorando a capacidade e a disponibilidade.</li>
</ul>
</li>
</ol>
<hr />
<h2><strong>Acoplamento estático</strong></h2>
<p>Os componentes em verde representam acoplamento estático.</p>
<p>O acoplamento estático ocorre quando um microservice depende de outro microservice em tempo de compilação ou configuração inicial do sistema.</p>
<p>Na maioria das vezes, acoplamentos estáticos são fáceis de identificar. Os mais comuns são:</p>
<ol>
<li><p><strong>Dependência de código</strong>: um microservice chama diretamente outro através de uma chamada de função ou API.</p>
</li>
<li><p><strong>Dependência de banco de dados</strong>: um microservice acessa diretamente o banco de dados de outra aplicação.</p>
</li>
<li><p><strong>Dependência de configuração</strong>: um micro-serviço depende de configurações específicas de outro microservice.</p>
</li>
</ol>
<p>Outra forma de acoplamento estático é quando um microservice depende de um contrato específico de outro. Por exemplo, um microservice espera que outro serviço retorne um objeto JSON específico. Se o contrato mudar, o microservice que depende dele pode quebrar, então é preciso atualizar a aplicação que depende do contrato.</p>
<hr />
<h2><strong>Acoplamento dinâmico</strong></h2>
<p>Os componentes em azul representam acoplamento dinâmico.</p>
<p>O acoplamento dinâmico ocorre quando um microservice depende de outro em tempo de execução.</p>
<p>Na maioria das vezes, acoplamentos dinâmicos são mais difíceis de identificar. Os mais comuns são:</p>
<ol>
<li><p><strong>Dependência de mensageria</strong>: um microservice envia uma mensagem para um barramento de mensagens e espera que outro serviço responda.</p>
</li>
<li><p><strong>Dependência de descoberta de serviço</strong>: um microservice consulta um serviço de descoberta para localizar outro.</p>
</li>
</ol>
<p>O interessante do acoplamento dinâmico é que ele permite que microservices sejam mais independentes e desacoplados. Se um microservice não estiver disponível, o que depende dele pode continuar funcionando, mesmo que de forma limitada.</p>
<hr />
<h2><strong>Conclusão</strong></h2>
<p>Na hora de projetar microservices, é importante considerar o acoplamento entre eles. Acoplamento estático e acoplamento dinâmico são duas formas de acoplamento que podem ser usadas em sistemas distribuídos.</p>
<p>Não existe uma regra rígida sobre qual tipo de acoplamento é melhor. Na verdade, a maioria dos sistemas de microservices terá uma combinação de acoplamento estático e acoplamento dinâmico.</p>
<p>Você como arquiteto precisa decidir qual tipo de acoplamento faz mais sentido para o problema que você está resolvendo no momento. Em alguns casos, acoplamento estático pode ser mais simples e eficiente. Em outros casos, acoplamento dinâmico pode ser mais flexível e resiliente.</p>
<p>O importante é entender as diferenças entre acoplamento estático e acoplamento dinâmico e saber quando usar cada um deles.</p>
<hr />
<h2><strong>Referências</strong></h2>
<ul>
<li><a href="https://www.amazon.com.br/Arquitetura-Software-Trade-off-Arquiteturas-Distribu%C3%ADdas/dp/8550819840">Arquitetura de Software: as Partes Difíceis: Análises Modernas de Trade-off Para Arquiteturas Distribuídas</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Event-driven Architecture (EDA)]]></title><description><![CDATA[Arquitetura de Software é um grande Trade-off no mundo da tecnologia, muitas vezes a escolha de uma arquitetura errada pode levar a um grande retrabalho e…]]></description><link>https://schons.hashnode.dev/event-driven-architecture-eda-2775822e52cf</link><guid isPermaLink="true">https://schons.hashnode.dev/event-driven-architecture-eda-2775822e52cf</guid><dc:creator><![CDATA[Luiz Schons]]></dc:creator><pubDate>Sat, 03 Aug 2024 19:22:34 GMT</pubDate><content:encoded><![CDATA[<p>Arquitetura de Software é um grande Trade-off no mundo da tecnologia, muitas vezes a escolha de uma arquitetura errada pode levar a um grande retrabalho e atraso no projeto. Sabendo disso, sempre é bom conhecer as arquiteturas mais utilizadas e entender o que cada uma tem de melhor para dessa forma escolher a que vai causar menos problemas no futuro, e esse é o grande segredo de uma boa arquitetura, prever o futuro.</p>
<p>Neste artigo vamos falar sobre a Arquitetura Orientada a Eventos (Event-driven Architecture — EDA), uma arquitetura que tem como principal característica a comunicação entre os serviços através de eventos.</p>
<h3>O que é EDA?</h3>
<p>A Arquitetura Orientada a Eventos (Event-driven Architecture — EDA) é um estilo de arquitetura de software que promove a produção, detecção, consumo e reação a eventos. Um evento pode ser qualquer coisa que seja notável ou significativa para o sistema, como alterações de estado, uma ação do usuário, um erro, uma mensagem recebida, entre outros.</p>
<p>Os eventos são mensagens assíncronas que são geradas por um produtor e consumidas por um ou mais consumidores. O produtor não precisa saber quem são os consumidores, e os consumidores não precisam saber quem são os produtores. Isso permite que os sistemas sejam desacoplados, escaláveis e flexíveis.</p>
<p>No nosso exemplo de hoje vamos simular o seguinte cenário:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586215514/fc4eed68-02f7-4b90-9ff9-8eb40d9c2fd0.png" alt="" style="display:block;margin:0 auto" />

<h3>Exemplo prático</h3>
<p>O sistema é composto por 4 serviços:</p>
<h4><strong>ecommerce-frontend:</strong></h4>
<ul>
<li><p>Frontend de um e-commerce que permite ao usuário adicionar produtos ao carrinho.</p>
</li>
<li><p>Usa React para criar a interface do usuário.</p>
</li>
</ul>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586217058/735c553b-591e-4b72-92dc-a186c441bae4.png" alt="" style="display:block;margin:0 auto" />

<h4><strong>stock-service:</strong></h4>
<ul>
<li><p>Serviço que gerencia o estoque dos produtos.</p>
</li>
<li><p>Usa PHP para comuncição com o RabbitMQ.</p>
</li>
</ul>
<p>channel(); \(channel\-&gt;queue\_declare('stock\_updated', false, false, false, false); \)msg = new AMQPMessage(json\_encode(\[ 'order\_id' =&gt; 1, 'status' =&gt; 'updated' \])); \(channel\-&gt;basic\_publish(\)msg, '', 'stock\_updated'); \(channel\-&gt;close(); \)connection\-&gt;close(); header('Content-Type: application/json'); echo json\_encode(\['success' =&gt; $success\]); } &gt; O código acima é somente um exemplo didático, não é um código funcional e nem seguro. #### payment-service: * Serviço que os pagamentos dos pedidos. * Usa Python para comunicação com o RabbitMQ. import pika import json import time max\_retries = 5 retry\_delay = 5 def establish\_connection(max\_retries, retry\_delay): for i in range(max\_retries): try: connection = pika.BlockingConnection(pika.ConnectionParameters('rabbitmq')) return connection except pika.exceptions.AMQPConnectionError: if i &lt; max\_retries - 1: time.sleep(retry\_delay) else: raise def process\_payment(order): print(f"Processando pagamento para o pedido: {order\['order\_id'\]}") return True def callback(ch, method, properties, body): order = json.loads(body) success = process\_payment(order) payment\_event = json.dumps({ 'order\_id': order\['order\_id'\], 'status': 'processed' if success else 'failed' }) try: channel.basic\_publish(exchange='', routing\_key='payment\_processed', body=payment\_event) print(f"Pagamento {'sucesso' if success else 'falha'} para o pedido: {order\['order\_id'\]}") except pika.exceptions.AMQPError as e: print(f"Erro ao publicar mensagem: {str(e)}") def setup\_channel(connection): channel = connection.channel() channel.queue\_declare(queue='stock\_updated') channel.queue\_declare(queue='payment\_processed') channel.basic\_consume(queue='stock\_updated', on\_message\_callback=callback, auto\_ack=True) return channel if \_\_name\_\_ == "\_\_main\_\_": connection = establish\_connection(max\_retries, retry\_delay) channel = setup\_channel(connection) print('Aguardando eventos de atualização de estoque...') try: channel.start\_consuming() except KeyboardInterrupt: print('Interrompido pelo usuário') finally: if connection: connection.close() &gt; O código acima é somente um exemplo didático, não é um código funcional e nem seguro. #### rabbitmq: * Servidor de mensageria que permite a comunicação entre os serviços. ![](<a href="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586219241/f89afcd2-0cc9-4390-837c-6fafa44d770a.png">https://cdn.hashnode.com/res/hashnode/image/upload/v1728586219241/f89afcd2-0cc9-4390-837c-6fafa44d770a.png</a>) O link para o repositório com o código completo está disponível [aqui](<a href="https://github.com/sschonss/event-driver">https://github.com/sschonss/event-driver</a>). ### Fluxo de comunicação 1. O usuário acessa o frontend do e-commerce e adiciona um produto ao carrinho. 2. O frontend envia uma requisição para o serviço de estoque para realizar o checkout do produto. 3. O serviço de estoque verifica se o produto está disponível e envia um evento de atualização de estoque para o RabbitMQ. 4. O serviço de pagamento consome o evento de atualização de estoque e processa o pagamento do pedido. 5. O serviço de pagamento envia um evento de pagamento processado para o RabbitMQ. ### Trade-offs Esse fluxo de comunicação é um exemplo simples de como a Arquitetura Orientada a Eventos (EDA) pode ser utilizada para desacoplar os serviços e permitir que eles se comuniquem de forma assíncrona e sem a necessidade de saber quem são os produtores ou consumidores dos eventos, tanto que foi por isso que resolvi usar diferentes linguagens de programação para cada serviço. Tudo isso permite que os serviços sejam escaláveis, flexíveis e resilientes, pois cada serviço é responsável por uma única tarefa e pode ser facilmente substituído ou atualizado sem afetar os outros serviços. Mas nem tudo são flores, a Arquitetura Orientada a Eventos (EDA) também tem suas desvantagens, como a complexidade de implementação, a necessidade de garantir a ordem dos eventos, a dificuldade de depurar problemas e a necessidade de garantir a entrega dos eventos. Por isso, é importante avaliar se a Arquitetura Orientada a Eventos (EDA) é a melhor escolha para o seu projeto e se você está disposto a lidar com as suas complexidades. ### Conclusão Essa foi somente uma introdução à Arquitetura Orientada a Eventos (EDA), uma arquitetura que tem como principal característica o desacoplamento dos serviços através de eventos assíncronos. Não existe uma arquitetura perfeita, cada uma tem suas vantagens e desvantagens, e cabe a você escolher a que melhor se adequa ao seu projeto e às suas necessidades. Espero que esse artigo tenha te ajudado a entender um pouco mais sobre a Arquitetura Orientada a Eventos (EDA) e como ela pode ser utilizada para criar sistemas escaláveis, flexíveis e resilientes. O link para o repositório com o código completo está disponível [aqui](<a href="https://github.com/sschonss/event-driver">https://github.com/sschonss/event-driver</a>). Se você tiver alguma dúvida ou sugestão, deixe nos comentários abaixo.</p>
]]></content:encoded></item><item><title><![CDATA[Algoritimo Dijkstra]]></title><description><![CDATA[Edsger W. Dijkstra foi um cientista da computação holandês que fez contribuições significativas para a ciência da computação. Ele é mais conhecido por…]]></description><link>https://schons.hashnode.dev/algoritimo-dijkstra-0d73b29ab3e5</link><guid isPermaLink="true">https://schons.hashnode.dev/algoritimo-dijkstra-0d73b29ab3e5</guid><dc:creator><![CDATA[Luiz Schons]]></dc:creator><pubDate>Mon, 29 Jul 2024 23:49:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586210788/4deec74c-6576-4022-b8f7-2c0cc9ef1e92.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Edsger W. Dijkstra foi um cientista da computação holandês que fez contribuições significativas para a ciência da computação. Ele é mais conhecido por desenvolver o algoritmo de Dijkstra, que resolve o problema do caminho menos custoso em um grafo direcionado ou não direcionado com arestas não negativas.</p>
<p>Mas antes de falar sobre o algoritmo de Dijkstra, vamos falar sobre o algoritmo de <code>Pesquisa em Largura</code>.</p>
<h3 id="heading-pesquisa-em-largura">Pesquisa em Largura</h3>
<p>Trabalhar com grafos é uma tarefa comum em computação. Um dos problemas mais comuns é encontrar o menor caminho entre dois vértices em um grafo. O algoritmo de <code>Pesquisa em Largura</code> mostra como encontrar o menor caminho em um grafo, como por exemplo, o caminho mais curto entre duas cidades em um mapa que tem cidades como vértices e estradas como arestas.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586193682/83906eab-e030-4187-ada0-a6fb599c5a92.png" alt /></p>
<p>Imagine que precisamos sair de A para chegar em G. O algoritmo de <code>Pesquisa em Largura</code> nos ajuda a encontrar o menor caminho. O algoritmo começa visitando o vértice de origem (A) e então explora todos os vértices vizinhos. Depois disso, explora os vértices que estão a dois passos de distância e assim por diante.</p>
<p>No nosso caso o menor caminho é <code>A -&gt; B -&gt; E -&gt; G</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586196340/21335b16-46e7-41d0-83ce-9616afcbc13c.png" alt /></p>
<p>Nesse caso, temos somente 3 <code>passos</code> para chegar em G.</p>
<h3 id="heading-algoritmo-de-dijkstra">Algoritmo de Dijkstra</h3>
<p>Mas e se as arestas tiverem pesos? O algoritmo de <code>Pesquisa em Largura</code> não funciona mais. O algoritmo de Dijkstra é um algoritmo que encontra o caminho mais <code>barato</code> em um grafo direcionado ou não direcionado, com arestas que possuem pesos. O algoritmo de Dijkstra mantém duas listas: uma lista de vértices visitados e uma lista de vértices não visitados. Ele seleciona o vértice não visitado mais próximo do vértice de origem, marca-o como visitado e atualiza os pesos dos vértices vizinhos.</p>
<p>Vamos adaptar o exemplo anterior para incluir pesos nas arestas.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586198835/e98599cc-e8c7-42f7-90fd-db6bc0585fe0.png" alt /></p>
<p>Agora temos pesos em cada aresta, e nosso desafio é encontrar o caminho em que a soma dos pesos seja menor.</p>
<p>Vamos aplicar o algoritmo de Dijkstra para encontrar o menor caminho de A para G.</p>
<ol>
<li>Começamos em A e visitamos todos os vértices vizinhos.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586200913/c8fb16dd-c96c-46f1-9631-8cc994f9e8c8.png" alt /></p>
<p>2. O próximo vértice mais próximo de A é C. Então visitamos C e atualizamos as distâncias dos vértices vizinhos.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586202833/605433b6-8e50-4537-a177-c1622ea93383.png" alt /></p>
<p>3. O próximo vértice mais próximo de A é D. Então visitamos D e atualizamos as distâncias dos vértices vizinhos.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586204480/9501673c-afae-4064-8bbc-07d604383605.png" alt /></p>
<p>4. O próximo vértice mais próximo de A é E. Então visitamos E e atualizamos as distâncias dos vértices vizinhos.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586206680/0b1d5fc0-12a1-45e9-9da0-d87c7527efa4.png" alt /></p>
<p>O menor caminho de A para G é <code>A -&gt; C -&gt; D -&gt; E -&gt; G</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586209257/72e9e802-5834-479a-856d-0a9cb6649c96.png" alt /></p>
<h3 id="heading-aplicando-isso-com-php">Aplicando isso com PHP</h3>
<p>Vamos criar um exemplo prático em PHP para aplicar o algoritmo de Dijkstra.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>
$graph = [
    <span class="hljs-string">'A'</span> =&gt; [<span class="hljs-string">'B'</span> =&gt; <span class="hljs-number">10</span>, <span class="hljs-string">'C'</span> =&gt; <span class="hljs-number">5</span>],
    <span class="hljs-string">'B'</span> =&gt; [<span class="hljs-string">'E'</span> =&gt; <span class="hljs-number">8</span>, <span class="hljs-string">'D'</span> =&gt; <span class="hljs-number">3</span>],
    <span class="hljs-string">'C'</span> =&gt; [<span class="hljs-string">'D'</span> =&gt; <span class="hljs-number">2</span>, <span class="hljs-string">'F'</span> =&gt; <span class="hljs-number">4</span>],
    <span class="hljs-string">'D'</span> =&gt; [<span class="hljs-string">'E'</span> =&gt; <span class="hljs-number">2</span>, <span class="hljs-string">'F'</span> =&gt; <span class="hljs-number">6</span>],
    <span class="hljs-string">'E'</span> =&gt; [<span class="hljs-string">'G'</span> =&gt; <span class="hljs-number">2</span>],
    <span class="hljs-string">'F'</span> =&gt; [<span class="hljs-string">'E'</span> =&gt; <span class="hljs-number">1</span>],
    <span class="hljs-string">'G'</span> =&gt; []
];

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">dijkstra</span>(<span class="hljs-params">$graph, $source, $target</span>)
</span>{
    $dist = [];
    $prev = [];
    $queue = <span class="hljs-keyword">new</span> <span class="hljs-built_in">SplPriorityQueue</span>();

    <span class="hljs-keyword">foreach</span> ($graph <span class="hljs-keyword">as</span> $vertex =&gt; $adj) {
        $dist[$vertex] = INF;
        $prev[$vertex] = <span class="hljs-literal">null</span>;
        $queue-&gt;insert($vertex, $vertex === $source ? <span class="hljs-number">0</span> : INF);
    }

    $dist[$source] = <span class="hljs-number">0</span>;

    <span class="hljs-keyword">while</span> (!$queue-&gt;isEmpty()) {
        $u = $queue-&gt;extract();

        <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">empty</span>($graph[$u])) {
            <span class="hljs-keyword">foreach</span> ($graph[$u] <span class="hljs-keyword">as</span> $v =&gt; $cost) {
                $alt = $dist[$u] + $cost;
                <span class="hljs-keyword">if</span> ($alt &lt; $dist[$v]) {
                    $dist[$v] = $alt;
                    $prev[$v] = $u;
                    $queue-&gt;insert($v, $alt);
                }
            }
        }
    }

    $path = [];
    $u = $target;
    <span class="hljs-keyword">while</span> (<span class="hljs-keyword">isset</span>($prev[$u])) {
        array_unshift($path, $u);
        $u = $prev[$u];
    }
    array_unshift($path, $source);

    <span class="hljs-keyword">return</span> $path;
}

$path = dijkstra($graph, <span class="hljs-string">'A'</span>, <span class="hljs-string">'G'</span>);
<span class="hljs-keyword">echo</span> implode(<span class="hljs-string">' -&gt; '</span>, $path);
</code></pre>
<p>Segue o passo a passo da execução do algoritmo:</p>
<ul>
<li><p>1. Inicializamos as distâncias e a fila de prioridade.</p>
</li>
<li><p>2. Inicializamos a distância do vértice de origem como 0.</p>
</li>
<li><p>3. Enquanto a fila de prioridade não estiver vazia, extraímos o vértice com a menor distância.</p>
</li>
<li><p>4. Para cada vértice vizinho, calculamos a distância alternativa e atualizamos a distância se for menor.</p>
</li>
<li><p>5. Construímos o caminho percorrendo os vértices anteriores. 6. Retornamos o caminho.</p>
<h2 id="heading-conclusao">Conclusão</h2>
<p>  Com o aumento do uso de AI, Machine Learning e Big Data, o conceito de grafos se tornou cada vez mais importante. O algoritmo de Dijkstra é um dos algoritmos mais importantes para encontrar o menor caminho em um grafo. Espero que você tenha entendido como o algoritmo de Dijkstra funciona e como ele pode ser aplicado em um cenário do mundo real. Se você gostou desse artigo, deixe um comentário e compartilhe com seus amigos. Até a próxima!</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Arquitetura Hexagonal e Mensageria com PHP]]></title><description><![CDATA[Link para o projeto: Existem diversas formas de se organizar um projeto, e uma das formas mais conhecidas é a arquitetura hexagonal. A arquitetura…]]></description><link>https://schons.hashnode.dev/arquitetura-hexagonal-e-mensageria-com-php-aa10a2148257</link><guid isPermaLink="true">https://schons.hashnode.dev/arquitetura-hexagonal-e-mensageria-com-php-aa10a2148257</guid><dc:creator><![CDATA[Luiz Schons]]></dc:creator><pubDate>Thu, 04 Jul 2024 01:11:37 GMT</pubDate><content:encoded><![CDATA[<h3 id="heading-arquiteura-hexagonal-ports-and-adapters">Arquiteura Hexagonal (Ports and Adapters)</h3>
<p>Link para o projeto: <a target="_blank" href="https://github.com/sschonss/microservices-hexagonal">https://github.com/sschonss/microservices-hexagonal</a></p>
<p>Existem diversas formas de se organizar um projeto, e uma das formas mais conhecidas é a arquitetura hexagonal. A arquitetura hexagonal é uma forma de organizar o projeto de forma que ele seja independente de frameworks, banco de dados, UI, etc. O objetivo é que o projeto seja independente de qualquer tecnologia, e que possa ser facilmente substituído.</p>
<p>Neste projeto, a arquitetura hexagonal foi implementada utilizando a linguagem de programação PHP e o framework Laravel. A ideia é que o projeto seja independente do Laravel, e dessa maneira, usamos somente o Laravel para criar rotas e controladores, e o restante do projeto é independente do Laravel.</p>
<h3 id="heading-estrutura-do-projeto">Estrutura do projeto</h3>
<p>O projeto foi dividido em 3 camadas principais:</p>
<ul>
<li><p><strong>Laravel</strong>: Toda a infraestrutura do projeto, como rotas, controladores, etc.</p>
</li>
<li><p><strong>Adapters</strong>: Camada responsável por conter os adaptadores de entrada e saída do projeto.</p>
</li>
<li><p><strong>Core</strong>: Camada responsável por conter as regras de negócio do projeto. Dentro do core, iremos trabalhar com Domain Driven Design (DDD).</p>
</li>
</ul>
<h3 id="heading-adapters">Adapters</h3>
<p>Os adapters são responsáveis por adaptar as entradas e saídas do projeto. Mas o que isso significa?</p>
<p>Imagine que você tem um projeto que precisa se comunicar com um banco de dados. Se você não utilizar uma camada de adapter, você terá que utilizar o banco de dados diretamente no seu projeto. Isso significa que você terá que utilizar as classes do banco de dados diretamente no seu projeto, e isso fará com que seu projeto fique dependente do banco de dados.</p>
<p>Então um ORM (Object Relational Mapping) é um exemplo de adapter. O ORM é uma camada que adapta a comunicação com o banco de dados, e dessa maneira, o projeto não fica dependente do banco de dados.</p>
<p>Dentro do nosso projeto, temos os seguintes adapters:</p>
<ul>
<li><p><strong>EmailAdapter</strong>: Adapter responsável por enviar e-mails.</p>
</li>
<li><p><strong>RabbitMQAdapter</strong>: Adapter responsável por enviar mensagens para o RabbitMQ.</p>
</li>
</ul>
<p>Neles contém as classes que adaptam a comunicação com o serviço de e-mail e com o RabbitMQ.</p>
<p>Tudo isso somente usando PHP sem Laravel, para que o projeto seja independente e possa ser facilmente substituído.</p>
<h3 id="heading-core">Core</h3>
<p>O core é a camada responsável por conter as regras de negócio do projeto. Dentro do core, iremos trabalhar com Domain Driven Design (DDD).</p>
<h3 id="heading-o-que-e-ddd">O que é DDD?</h3>
<p>Domain Driven Design é uma forma de organizar o projeto de forma que ele seja separado por domínios. Cada domínio é uma parte do projeto que contém as regras de negócio. Dessa maneira, o projeto fica organizado e fácil de ser mantido.</p>
<p>Dentro do core, temos os seguintes domínios:</p>
<ul>
<li><p>Email: Domínio responsável por emails em geral da aplicação.</p>
</li>
<li><p>Connections: Domínio responsável por conexões com serviços externos, como RabbitMQ, Redis, etc.</p>
</li>
</ul>
<p>Dentro de cada domínio, teremos as camadas responsáveis por conter as regras de negócio para cada feature.</p>
<p>O mais importante é que o core não depende de nada. Ele é independente de qualquer framework, banco de dados, etc. Dessa maneira, podemos facilmente substituir o Laravel por outro framework, ou o MySQL por outro banco de dados.</p>
<p>O Core se comunica com serviços externos através dos adapters. Dessa maneira, o core não fica dependente de nenhum serviço externo, somente do adapter.</p>
<h3 id="heading-projeto">Projeto</h3>
<p>O projeto é um exemplo simples de como a arquitetura hexagonal pode ser implementada. O projeto é um sistema de cadastro de usuários, onde o usuário pode se cadastrar e receber um e-mail de boas-vindas.</p>
<p>O projeto foi dividido em 3 serviços:</p>
<ul>
<li><p>Main App: Serviço principal, onde o usuário se cadastra. Nele está a arquitetura hexagonal.</p>
</li>
<li><p>RabbitMQ: Serviço de mensageria, onde o Main App envia uma mensagem para o RabbitMQ.</p>
</li>
<li><p>Email App: Serviço de e-mail, onde lê as mensagens do RabbitMQ e envia um e-mail para o usuário.</p>
</li>
</ul>
<p>Dessa maneira, o Main App não fica dependente do RabbitMQ e do serviço de e-mail. Ele somente envia uma mensagem para o RabbitMQ, e o RabbitMQ envia a mensagem para o serviço de e-mail.</p>
<h3 id="heading-como-rodar-o-projeto">Como rodar o projeto</h3>
<p>Para rodar o projeto, você precisa ter o Composer, Docker e o Docker Compose instalados na sua máquina.</p>
<p>Após instalar o Docker e o Docker Compose, basta rodar o seguinte comando:</p>
<p><code>cd main-app   composer install   ./vendor/bin/sail up</code></p>
<p>Após rodar o comando, o projeto estará rodando na porta 80.</p>
<p>Já podemos criar um usuário através do endpoint <code>POST /api/user</code>, passando o seguinte JSON:</p>
<p><code>{   "name": "John Doe",   "email": "john@doe.com",   "password": "123456"   }</code></p>
<p>Após criar o usuário, será disparado um evento para o RabbitMQ, mas o serviço de e-mail ainda não está rodando. Para rodar o serviço de e-mail, basta rodar o seguinte comando:</p>
<p><code>cd email-app   docker-compose up --build --force-recreate</code></p>
<p>Após rodar o comando, o serviço de e-mail estará rodando e irá ler as mensagens do RabbitMQ e enviar um e-mail para o usuário.</p>
<h3 id="heading-acessar-o-rabbitmq">Acessar o RabbitMQ</h3>
<p>Para acessar o RabbitMQ, basta acessar o seguinte endereço:</p>
<p><code>http://localhost:15672</code></p>
<p>E fazer login com as seguintes credenciais:</p>
<p><code>Username: guest   Password: guest</code></p>
<h3 id="heading-conclusao">Conclusão</h3>
<p>A arquitetura hexagonal é uma forma de organizar o projeto de forma que ele seja independente de qualquer tecnologia. Neste projeto, a arquitetura hexagonal foi implementada utilizando a linguagem de programação PHP e o framework Laravel. A ideia é que o projeto seja independente do Laravel, e dessa maneira, usamos somente o Laravel para criar rotas e controladores, e o restante do projeto é independente do Laravel.</p>
<p>O projeto é um exemplo simples de como a arquitetura hexagonal pode ser implementada. O projeto é um sistema de cadastro de usuários, onde o usuário pode se cadastrar e receber um e-mail de boas-vindas.</p>
<p>Também foi implementado um serviço de mensageria com RabbitMQ e um serviço de e-mail. Dessa maneira, o Main App não fica dependente do RabbitMQ e do serviço de e-mail. Ele somente envia uma mensagem para o RabbitMQ, e o RabbitMQ envia a mensagem para o serviço de e-mail.</p>
<p>Vimos como a mensageria ajuda a desacoplar os serviços, e como a arquitetura hexagonal ajuda a organizar o projeto de forma que ele seja independente de qualquer tecnologia.</p>
]]></content:encoded></item><item><title><![CDATA[Microservices e Mensageria com PHP, NodeJS, RabbitMQ e Docker]]></title><description><![CDATA[O desenvolvimento de microserviços é uma abordagem arquitetural que estrutura uma aplicação como um conjunto de serviços pequenos e independentes, que são…]]></description><link>https://schons.hashnode.dev/microservices-e-mensageria-com-php-nodejs-rabbitmq-e-docker-7266127c8489</link><guid isPermaLink="true">https://schons.hashnode.dev/microservices-e-mensageria-com-php-nodejs-rabbitmq-e-docker-7266127c8489</guid><dc:creator><![CDATA[Luiz Schons]]></dc:creator><pubDate>Sat, 29 Jun 2024 15:05:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586225558/2aac07c7-f37b-4a91-bb14-5e4d7656ea8a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>O desenvolvimento de microserviços é uma abordagem arquitetural que estrutura uma aplicação como um conjunto de serviços pequenos e independentes, que são executados em seu próprio processo e se comunicam por meio de protocolos leves, como HTTP, WebSockets ou AMQP.</p>
<p>Neste artigo, vamos criar uma aplicação de microserviços com PHP, RabbitMQ e Docker. O RabbitMQ é um software de mensageria que implementa o protocolo AMQP (Advanced Message Queuing Protocol), que é um protocolo de mensagens assíncronas.</p>
<h3 id="heading-o-que-e-rabbitmq">O que é RabbitMQ?</h3>
<p>Vamos imaginar o seguinte cenário: você tem um sistema que precisa enviar e-mails para os usuários. Em vez de enviar os e-mails diretamente, você pode enviar uma mensagem para uma fila de mensagens, que será consumida por um serviço que envia os e-mails. Isso é o que o RabbitMQ faz: ele recebe mensagens de produtores e as envia para consumidores.</p>
<p>Dessa forma é possível desacoplar a produção de mensagens do consumo, o que permite escalar cada parte do sistema de forma independente. Além disso, o RabbitMQ garante que as mensagens sejam entregues na ordem correta e que não sejam perdidas.</p>
<h3 id="heading-arquitetura-da-aplicacao">Arquitetura da aplicação</h3>
<p>Nossa aplicação será composta por dois microserviços: um produtor e um consumidor. O produtor será responsável por enviar mensagens para uma fila de mensagens, e o consumidor será responsável por consumir as mensagens e exibi-las no terminal.</p>
<p>Nosso primeiro passo será criar o arquivo <code>docker-compose.yml</code> para definir os serviços da nossa aplicação:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">services:</span>
  <span class="hljs-attr">rabbitmq:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">"rabbitmq:3-management"</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">"rabbitmq"</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"15672:15672"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"5672:5672"</span>

  <span class="hljs-attr">php-app:</span>
    <span class="hljs-attr">build:</span> <span class="hljs-string">./php-app</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">rabbitmq</span>

  <span class="hljs-attr">node-app:</span>
    <span class="hljs-attr">build:</span> <span class="hljs-string">./node-app</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">rabbitmq</span>
</code></pre>
<p>Neste arquivo, definimos três serviços: <code>rabbitmq</code>, <code>php-app</code> e <code>node-app</code>. O serviço <code>rabbitmq</code> é baseado na imagem <code>rabbitmq:3-management</code>, que inclui a interface de gerenciamento do RabbitMQ. Os serviços <code>php-app</code> e <code>node-app</code> são baseados em imagens customizadas que iremos criar.</p>
<h3 id="heading-criando-o-produtor-com-php">Criando o produtor com PHP</h3>
<p>Agora, vamos criar o diretório <code>php-app</code> e o arquivo <code>Dockerfile</code> dentro dele:</p>
<pre><code class="lang-bash">mkdir php-app &amp;&amp; touch php-app/Dockerfile
</code></pre>
<p>No arquivo <code>Dockerfile</code>, vamos definir a imagem base e copiar os arquivos da aplicação:</p>
<pre><code class="lang-dockerfile"><span class="hljs-keyword">FROM</span> php:<span class="hljs-number">7.4</span>-cli

<span class="hljs-keyword">RUN</span><span class="bash"> apt-get update &amp;&amp; apt-get install -y librabbitmq-dev libssl-dev git unzip wget curl</span>

<span class="hljs-keyword">RUN</span><span class="bash"> docker-php-ext-install sockets pdo pdo_mysql</span>
<span class="hljs-keyword">RUN</span><span class="bash"> pecl install amqp &amp;&amp; docker-php-ext-enable amqp</span>

<span class="hljs-keyword">COPY</span><span class="bash"> . /usr/src/myapp</span>
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /usr/src/myapp</span>

<span class="hljs-keyword">RUN</span><span class="bash"> curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/<span class="hljs-built_in">local</span>/bin --filename=composer</span>

<span class="hljs-keyword">RUN</span><span class="bash"> composer clear-cache</span>

<span class="hljs-keyword">RUN</span><span class="bash"> composer install</span>

<span class="hljs-keyword">CMD</span><span class="bash"> [<span class="hljs-string">"php"</span>, <span class="hljs-string">"./publisher.php"</span>]</span>
</code></pre>
<p>Neste arquivo, definimos a imagem base <code>php:7.4-cli</code> e instalamos as dependências necessárias para o RabbitMQ e o Composer. Em seguida, copiamos os arquivos da aplicação para o diretório <code>/usr/src/myapp</code>, instalamos as dependências do Composer e executamos o script <code>publisher.php</code>.</p>
<p>Agora, vamos criar o arquivo <code>publisher.php</code> na raiz do projeto:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>
<span class="hljs-keyword">require_once</span> <span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/vendor/autoload.php'</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">PhpAmqpLib</span>\<span class="hljs-title">Connection</span>\<span class="hljs-title">AMQPStreamConnection</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">PhpAmqpLib</span>\<span class="hljs-title">Message</span>\<span class="hljs-title">AMQPMessage</span>;

$maxRetries = <span class="hljs-number">5</span>;
$retryDelay = <span class="hljs-number">5</span>;

<span class="hljs-keyword">for</span> ($attempt = <span class="hljs-number">0</span>; $attempt &lt; $maxRetries; $attempt++) {
    <span class="hljs-keyword">try</span> {
        $connection = <span class="hljs-keyword">new</span> AMQPStreamConnection(<span class="hljs-string">'rabbitmq'</span>, <span class="hljs-number">5672</span>, <span class="hljs-string">'guest'</span>, <span class="hljs-string">'guest'</span>);
        $channel = $connection-&gt;channel();
        <span class="hljs-keyword">break</span>;
    } <span class="hljs-keyword">catch</span> (<span class="hljs-built_in">Exception</span> $e) {
        <span class="hljs-keyword">echo</span> <span class="hljs-string">"Failed to connect to RabbitMQ. Retrying in <span class="hljs-subst">$retryDelay</span> seconds...\n"</span>;
        sleep($retryDelay);
    }    
}

<span class="hljs-keyword">if</span> (!<span class="hljs-keyword">isset</span>($connection)) {
    <span class="hljs-keyword">echo</span> <span class="hljs-string">"Not possible to connect to RabbitMQ. Exiting...\n The application will be restarted by Docker\n"</span>;
    <span class="hljs-keyword">exit</span>(<span class="hljs-number">1</span>);
}

$channel-&gt;queue_declare(<span class="hljs-string">'hello'</span>, <span class="hljs-literal">false</span>, <span class="hljs-literal">false</span>, <span class="hljs-literal">false</span>, <span class="hljs-literal">false</span>);

$msg = <span class="hljs-keyword">new</span> AMQPMessage(<span class="hljs-string">'Hello, RabbitMQ! Now is '</span> . date(<span class="hljs-string">'Y-m-d H:i:s'</span>));
$channel-&gt;basic_publish($msg, <span class="hljs-string">''</span>, <span class="hljs-string">'hello'</span>);

<span class="hljs-keyword">echo</span> <span class="hljs-string">" [x] Sent 'Hello, RabbitMQ!'\n"</span>;

$channel-&gt;close();
$connection-&gt;close();
</code></pre>
<p>Neste arquivo, criamos uma conexão com o RabbitMQ, declaramos uma fila chamada `hello` e enviamos uma mensagem para essa fila. Em seguida, fechamos a conexão com o RabbitMQ. Perceba que estamos tentando conectar ao RabbitMQ várias vezes, com um intervalo de 5 segundos entre as tentativas.</p>
<p>Isso é importante para garantir que a aplicação consiga se conectar ao RabbitMQ mesmo que ele não esteja disponível imediatamente. Isso é uma prática comum em aplicações distribuídas, onde a disponibilidade dos serviços pode variar ao longo do tempo, pesquise mais sobre `circuit breaker` e `retry pattern`.</p>
<p>Mas para instalar as dependências do RabbitMQ e do Composer, precisamos criar o arquivo `composer.json` na raiz do projeto:</p>
<pre><code class="lang-json">{ <span class="hljs-attr">"require"</span>: { <span class="hljs-attr">"php-amqplib/php-amqplib"</span>: <span class="hljs-string">"^3.1"</span>, <span class="hljs-attr">"phpseclib/phpseclib"</span>: <span class="hljs-string">"^3.0"</span> } }
</code></pre>
<h2 id="heading-criando-o-consumidor-com-nodejs">Criando o consumidor com Node.js</h2>
<p>Agora, vamos criar o diretório `node-app` e o arquivo `Dockerfile` dentro dele:</p>
<pre><code class="lang-bash">mkdir node-app &amp;&amp; touch node-app/Dockerfile
</code></pre>
<p>No arquivo `Dockerfile`, vamos definir a imagem base e copiar os arquivos da aplicação:</p>
<pre><code class="lang-dockerfile"><span class="hljs-keyword">FROM</span> node:<span class="hljs-number">14</span> 
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /usr/src/app </span>
<span class="hljs-keyword">COPY</span><span class="bash"> package\*.json ./ </span>
<span class="hljs-keyword">RUN</span><span class="bash"> npm install </span>
<span class="hljs-keyword">COPY</span><span class="bash"> . . </span>
<span class="hljs-keyword">CMD</span><span class="bash"> \[<span class="hljs-string">"node"</span>, <span class="hljs-string">"subscriber.js"</span>\]</span>
</code></pre>
<p>Neste arquivo, definimos a imagem base `node:14`, copiamos os arquivos da aplicação para o diretório `/usr/src/app`, instalamos as dependências do Node.js e executamos o script `subscriber.js`.</p>
<p>Agora, vamos criar o arquivo `subscriber.js` na raiz do projeto:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> amqp = <span class="hljs-built_in">require</span>(<span class="hljs-string">'amqplib'</span>);

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">connectWithRetry</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> maxRetries = <span class="hljs-number">5</span>;
    <span class="hljs-keyword">const</span> retryDelay = <span class="hljs-number">5000</span>;

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> attempt = <span class="hljs-number">1</span>; attempt &lt;= maxRetries; attempt++) {
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">const</span> connection = <span class="hljs-keyword">await</span> amqp.connect(<span class="hljs-string">'amqp://rabbitmq'</span>);
            <span class="hljs-keyword">return</span> connection;
        } <span class="hljs-keyword">catch</span> (err) {
            <span class="hljs-built_in">console</span>.error(<span class="hljs-string">`Not possible to connect to RabbitMQ. Retrying in <span class="hljs-subst">${retryDelay}</span>ms. Attempt <span class="hljs-subst">${attempt}</span> of <span class="hljs-subst">${maxRetries}</span>`</span>);
            <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function"><span class="hljs-params">resolve</span> =&gt;</span> <span class="hljs-built_in">setTimeout</span>(resolve, retryDelay));
        }
    }

    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`Failed to connect to RabbitMQ after <span class="hljs-subst">${maxRetries}</span> attempts`</span>);
}

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">receiveMessages</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> connection = <span class="hljs-keyword">await</span> connectWithRetry();
        <span class="hljs-keyword">const</span> channel = <span class="hljs-keyword">await</span> connection.createChannel();
        <span class="hljs-keyword">const</span> queue = <span class="hljs-string">'hello'</span>;

        <span class="hljs-keyword">await</span> channel.assertQueue(queue, { <span class="hljs-attr">durable</span>: <span class="hljs-literal">false</span> });

        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">" [*] Waiting for messages in %s."</span>, queue);

        channel.consume(queue, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">msg</span>) </span>{
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">" [x] Received: %s"</span>, msg.content.toString());
        }, {
            <span class="hljs-attr">noAck</span>: <span class="hljs-literal">true</span>
        });

    } <span class="hljs-keyword">catch</span> (err) {
        <span class="hljs-built_in">console</span>.error(err.message);
        process.exit(<span class="hljs-number">1</span>);
    }
}

receiveMessages();
</code></pre>
<p>Neste arquivo, criamos uma conexão com o RabbitMQ, declaramos uma fila chamada `hello` e consumimos as mensagens dessa fila.</p>
<p>Em seguida, exibimos as mensagens no terminal. Assim como no produtor, estamos tentando conectar ao RabbitMQ várias vezes, com um intervalo de 5 segundos entre as tentativas. Isso é importante para garantir que o consumidor consiga se conectar ao RabbitMQ mesmo que ele não esteja disponível imediatamente.</p>
<p>E para instalar as dependências do RabbitMQ, precisamos criar o arquivo `package.json` na raiz do projeto:</p>
<pre><code class="lang-json">{ <span class="hljs-attr">"name"</span>: <span class="hljs-string">"node-app"</span>, <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>, <span class="hljs-attr">"description"</span>: <span class="hljs-string">""</span>, <span class="hljs-attr">"main"</span>: <span class="hljs-string">"subscriber.js"</span>, <span class="hljs-attr">"dependencies"</span>: { <span class="hljs-attr">"amqplib"</span>: <span class="hljs-string">"^0.8.0"</span> }, <span class="hljs-attr">"author"</span>: <span class="hljs-string">""</span>, <span class="hljs-attr">"license"</span>: <span class="hljs-string">"ISC"</span> }
</code></pre>
<h2 id="heading-executando-a-aplicacao">Executando a aplicação</h2>
<p>Agora que criamos os serviços do RabbitMQ, do produtor e do consumidor, vamos executar a aplicação com o Docker Compose:</p>
<pre><code class="lang-bash">docker compose up --build
</code></pre>
<p>Isso irá criar os containers dos serviços e executar a aplicação. Você verá as mensagens enviadas pelo produtor sendo exibidas no terminal pelo consumidor.</p>
<p>O RabbitMQ também estará disponível na porta 15672, onde você pode acessar a interface de gerenciamento e visualizar as filas e as mensagens.</p>
<p>Com isso, criamos uma aplicação de microserviços com PHP, RabbitMQ e Docker. Essa abordagem arquitetural permite escalar cada parte do sistema de forma independente e garante a entrega das mensagens na ordem correta.</p>
<p>Espero que este artigo tenha sido útil e que você possa aplicar esses conceitos em seus projetos. Se tiver alguma dúvida ou sugestão, deixe nos comentários.</p>
<p>Qualquer dúvida, estou à disposição.</p>
<p>Link do Repositório: <a target="_blank" href="https://github.com/sschonss/ms-rabbitmq">Microservices e Mensageria com PHP, RabbitMQ e Docker</a></p>
]]></content:encoded></item><item><title><![CDATA[PHP — Streams Filters]]></title><description><![CDATA[Se você já trabalhou com arquivos em PHP, provavelmente já usou funções como fopen, fwrite, fread, fclose, entre outras. Streams são uma abstração muito…]]></description><link>https://schons.hashnode.dev/php-streams-filters-d2c681cbec6d</link><guid isPermaLink="true">https://schons.hashnode.dev/php-streams-filters-d2c681cbec6d</guid><dc:creator><![CDATA[Luiz Schons]]></dc:creator><pubDate>Tue, 04 Jun 2024 01:11:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586299110/eaf9b102-5a29-4217-add6-f8104bd38836.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Se você já trabalhou com arquivos em PHP, provavelmente já usou funções como <code>fopen</code>, <code>fwrite</code>, <code>fread</code>, <code>fclose</code>, entre outras. Streams são uma abstração muito poderosa e flexível para trabalhar com arquivos e outros recursos de I/O.</p>
<p>Streams são uma forma de abstrair a manipulação de dados de entrada e saída, permitindo que você leia e escreva dados de e para diferentes fontes, como arquivos, strings, conexões de rede, etc.</p>
<p>Por exemplo, você pode usar streams para ler dados de um arquivo, processá-los e escrevê-los em outro arquivo, sem ter que se preocupar com a origem ou destino dos dados.</p>
<p>Ou então, você pode usar streams para ler dados de uma conexão de rede, processá-los e escrevê-los em um banco de dados. E assim por diante.</p>
<h3 id="heading-exemplo-basico">Exemplo básico</h3>
<p>$stream = fopen('data.txt', 'r');<br />while (!feof($stream)) {<br />    $line = fgets($stream);<br />    echo $line;<br />}<br />fclose($stream);</p>
<p>Neste exemplo, abrimos um arquivo chamado <code>data.txt</code> em modo de leitura (<code>'r'</code>). Em seguida, lemos o conteúdo do arquivo linha por linha até o final do arquivo (<code>feof($stream)</code>). Por fim, fechamos o arquivo com <code>fclose($stream)</code>.</p>
<p>Mas o que é um stream? Um stream é um recurso que representa uma fonte ou destino de dados. No exemplo acima, <code>$stream</code> é um stream que representa o arquivo <code>data.txt</code>.</p>
<p>Vamos ver um exemplo mais avançado.</p>
<h3 id="heading-exemplo-avancado">Exemplo avançado</h3>
<p>$context = stream_context_create([<br />    'http' =&gt; [<br />        'method' =&gt; 'POST',<br />        'header' =&gt; 'Content-Type: application/json',<br />        'content' =&gt; json_encode(['key' =&gt; 'value']),<br />    ],<br />]);  </p>
<p>$stream = fopen('http://example.com/data.csv', 'r', false, $context);<br />while (!feof($stream)) {<br />    $line = fgetcsv($stream);<br />    print_r($line);<br />}<br />fclose($stream);</p>
<p>Neste exemplo, criamos um contexto de stream com <code>stream_context_create</code>, que é um array associativo com opções de configuração para o stream. No caso, estamos configurando um stream HTTP com o método <code>POST</code>, o cabeçalho <code>Content-Type: application/json</code> e o corpo da requisição em formato JSON.</p>
<p>Em seguida, abrimos um stream para a URL <code>http://example.com/data.csv</code> em modo de leitura (<code>'r'</code>) com o contexto de stream que acabamos de criar.</p>
<p>Depois, lemos o conteúdo do stream linha por linha com <code>fgetcsv</code>, que lê uma linha do stream e a converte em um array de valores separados por vírgula.</p>
<p>Por fim, fechamos o stream com <code>fclose</code>.</p>
<h3 id="heading-mas-por-que-usar-streams">Mas por que usar streams?</h3>
<p>O PHP surgiu em uma época em que a manipulação de arquivos era a principal forma de interagir com o sistema de arquivos e outros recursos de I/O. Por isso, as funções de manipulação de arquivos do PHP são baseadas em operações de baixo nível, como abrir, ler, escrever e fechar arquivos, os famosos <code>fopen</code>, <code>fread</code>, <code>fwrite</code> e <code>fclose</code>.</p>
<p>Com o tempo, a necessidade de interagir com outros tipos de recursos de I/O, como conexões de rede, bancos de dados, etc., tornou-se cada vez mais comum. E foi aí que os streams entraram em cena.</p>
<p>Streams são essencialmente uma camada de abstração sobre diferentes tipos de recursos de I/O, permitindo que você leia e escreva dados de e para esses recursos de forma consistente, independente da origem ou destino dos dados.</p>
<p>Quando eu falo de recursos de I/O, estou me referindo a qualquer coisa que você possa ler ou escrever dados, como arquivos, strings, janelas de console, conexões de rede, sockets, pipes, etc.</p>
<p>E quando eu falei de não se preocupar com a origem ou destino dos dados, eu quis dizer que você não precisa saber se está lendo de um arquivo, de uma conexão de rede, de um banco de dados, etc. Você simplesmente lê os dados do stream e os processa da forma que desejar. Isso é muito poderoso e flexível.</p>
<h3 id="heading-e-como-eu-uso-streams">E como eu uso streams?</h3>
<p>Para usar streams em PHP, você precisa entender alguns conceitos básicos:</p>
<ul>
<li><strong>Resource</strong></li>
<li><strong>Context</strong></li>
<li><strong>Wrapper</strong></li>
<li><strong>Stream functions</strong></li>
<li><strong>Stream filters</strong></li>
</ul>
<blockquote>
<p><strong><em>Nota*</em></strong>: Este é apenas um resumo básico sobre streams em PHP. Para saber mais, consulte a<em> [</em>documentação oficial<em>](https://www.php.net/manual/en/book.stream.php)</em>.*</p>
</blockquote>
<h3 id="heading-resource">Resource</h3>
<p>Vamos nos aprofundar um pouco mais no conceito de recurso (resource) em PHP.</p>
<p>Um stream é representado por um recurso (resource) em PHP. Um recurso é uma variável especial que contém uma referência interna para um recurso externo, como um arquivo, uma conexão de rede, etc. Você pode criar um recurso com a função <code>fopen</code> e fechá-lo com a função <code>fclose</code>.</p>
<p>Um <code>resource</code> em PHP é indentificador interno para recursos externos, e a função <code>get_resource_type</code> pode ser usada para obter o tipo de recurso.</p>
<p>$stream = fopen('data.txt', 'r');<br />echo get_resource_type($stream);<br />fclose($stream);</p>
<p>No console, você verá algo como <code>stream</code>.</p>
<p>O <code>resource</code> sempre vai referenciar o arquivo aberto.</p>
<h3 id="heading-context">Context</h3>
<p>Um contexto de stream é um array associativo com opções de configuração para o stream. Você pode criar um contexto de stream com a função <code>stream_context_create</code> e passá-lo como argumento para funções que abrem streams, como <code>fopen</code>, <code>file_get_contents</code>, etc.</p>
<p>Quando você abre um stream com um contexto de stream, as opções de configuração do contexto são aplicadas ao stream. Por exemplo, você pode configurar um stream HTTP com o método <code>POST</code>, o cabeçalho <code>Content-Type: application/json</code>, etc.</p>
<p>$context = stream_context_create([<br />    'http' =&gt; [<br />        'method' =&gt; 'POST',<br />        'header' =&gt; 'Content-Type: application/json',<br />        'content' =&gt; json_encode(['key' =&gt; 'value']),<br />    ],<br />]);  </p>
<p>$stream = fopen('http://example.com/data.csv', 'r', false, $context);</p>
<p>Ou então, você pode usar um contexto de stream para configurar um stream FTP com o nome de usuário e senha.</p>
<p>$context = stream_context_create([<br />    'ftp' =&gt; [<br />        'username' =&gt; 'user',<br />        'password' =&gt; 'pass',<br />    ],<br />]);  </p>
<p>$stream = fopen('ftp://example.com/data.csv', 'r', false, $context);</p>
<h3 id="heading-wrapper">Wrapper</h3>
<p>Um wrapper é um esquema de URL que define como o PHP deve abrir um stream. Por exemplo, o wrapper <code>http</code> é usado para abrir streams HTTP, o wrapper <code>ftp</code> é usado para abrir streams FTP, etc.</p>
<p>Quando você abre um stream com <code>fopen</code>, o PHP usa o wrapper correspondente para abrir o stream. Por exemplo, se você abrir um stream <code>http://example.com/data.csv</code>, o PHP usará o wrapper <code>http</code> para abrir o stream.</p>
<p>Você também pode usar wrappers para abrir streams de outros tipos de recursos, como strings, variáveis de memória, etc.</p>
<p>$stream = fopen('data:text/plain;base64,SGVsbG8gV29ybGQ=', 'r');<br />echo stream_get_contents($stream);<br />fclose($stream);</p>
<p>Neste exemplo, estamos abrindo um stream de uma string codificada em base64 com o wrapper <code>data</code>. O conteúdo da string é <code>Hello World</code>.</p>
<p>$stream = fopen('php://memory', 'r+');</p>
<p>Neste exemplo, estamos abrindo um stream de uma variável de memória com o wrapper <code>php</code>. O stream é aberto em modo de leitura e escrita (<code>'r+'</code>).</p>
<p>$stream = fopen('ogg://temp', 'r+');</p>
<p>OGG é um wrapper que permite a manipulação de arquivos de áudio no formato OGG, isso é um exemplo de um wrapper personalizado.</p>
<p>Você pode criar seus próprios wrappers personalizados para abrir streams de outros tipos de recursos, como bancos de dados, APIs, etc.</p>
<p>Veja a <a target="_blank" href="https://www.php.net/manual/en/wrappers.php">documentação oficial</a> para mais informações sobre wrappers em PHP.</p>
<h3 id="heading-stream-functions">Stream functions</h3>
<p>O PHP fornece várias funções para trabalhar com streams, como <code>fopen</code>, <code>fclose</code>, <code>fread</code>, <code>fwrite</code>, <code>feof</code>, <code>fseek</code>, <code>ftell</code>, <code>fgetcsv</code>, <code>stream_get_contents</code>, etc.</p>
<p>Você pode usar essas funções para abrir, fechar, ler, escrever, posicionar, verificar o final, etc., de um stream.</p>
<p>Por exemplo, você pode usar <code>fopen</code> para abrir um stream, <code>fread</code> para ler dados do stream, <code>fwrite</code> para escrever dados no stream e <code>fclose</code> para fechar o stream.</p>
<p>$stream = fopen('data.txt', 'r');  </p>
<p>while (!feof($stream)) {<br />    $line = fgets($stream);<br />    echo $line;<br />}  </p>
<p>fwrite($stream, 'Hello World');  </p>
<p>fclose($stream);p</p>
<p>Neste exemplo, estamos abrindo um arquivo chamado <code>data.txt</code> em modo de leitura (<code>'r'</code>). Em seguida, lemos o conteúdo do arquivo linha por linha até o final do arquivo com <code>fgets</code>. Depois, escrevemos a string <code>Hello World</code> no arquivo com <code>fwrite</code>. Por fim, fechamos o arquivo com <code>fclose</code>.</p>
<p>Você pode usar essas funções para trabalhar com streams de diferentes tipos de recursos, como o curl, sockets, pipes, etc.</p>
<p>$url = 'http://example.com/data/page.html';  </p>
<p>$data = curl_init($url);  </p>
<p>curl_setopt($data, CURLOPT_RETURNTRANSFER, true);  </p>
<p>$response = curl_exec($data);  </p>
<p>curl_close($data);  </p>
<p>$stream = fopen('php://memory', 'r+');  </p>
<p>fwrite($stream, $response);  </p>
<p>rewind($stream);  </p>
<p>echo stream_get_contents($stream);  </p>
<p>fclose($stream);</p>
<p>Neste exemplo, estamos fazendo uma requisição HTTP com o curl para a URL <code>http://example.com/data/page.html</code>. Em seguida, abrimos um stream de uma variável de memória com o wrapper <code>php</code> em modo de leitura e escrita (<code>'r+'</code>). Depois, escrevemos a resposta da requisição no stream com <code>fwrite</code>. Por fim, exibimos o conteúdo do stream com <code>stream_get_contents</code> e fechamos o stream com <code>fclose</code>.</p>
<h3 id="heading-stream-filters">Stream filters</h3>
<p>Aqui chegamos a um dos recursos mais poderosos e flexíveis dos streams em PHP: os filtros de stream.</p>
<p>Imagine o seguinte cenário: você está lendo dados de um arquivo e precisa processar esses dados antes de exibi-los na tela. Com os filtros de stream, você pode criar um filtro que processa os dados conforme necessário e aplicá-lo ao stream de leitura, sem precisar modificar o código que lê os dados.</p>
<p>Por exemplo, você pode usar um filtro de stream para converter os dados de um arquivo de texto para letras maiúsculas antes de exibi-los na tela.</p>
<p>$stream = fopen('data.txt', 'r');  </p>
<p>stream_filter_append($stream, 'string.toupper');  </p>
<p>echo stream_get_contents($stream);  </p>
<p>fclose($stream);</p>
<p>Neste exemplo, estamos abrindo um arquivo chamado <code>data.txt</code> em modo de leitura (<code>'r'</code>). Em seguida, aplicamos o filtro <code>string.toupper</code> ao stream com <code>stream_filter_append</code>, que converte os dados para letras maiúsculas. Por fim, exibimos o conteúdo do stream com <code>stream_get_contents</code> e fechamos o arquivo com <code>fclose</code>.</p>
<p>Essa conversão é feita automaticamente pelo filtro de stream, ocorre durante a leitura dos dados e não afeta o arquivo original.</p>
<p>Você pode criar seus próprios filtros de stream para processar os dados de acordo com suas necessidades.</p>
<p>Veja a <a target="_blank" href="https://www.php.net/manual/en/filters.php">documentação oficial</a> para mais informações sobre filtros de stream em PHP.</p>
<h3 id="heading-filterphp">FilterPHP</h3>
<p>Mas eu preparei um exemplo para você entender melhor como funciona um filtro de stream.</p>
<p>No primeiro exemplo, vamos criar um filtro de stream que filtrará somente para linhas que contém a palavra “PHP”.</p>
<p>&lt;?php  </p>
<p>class FilterPHP extends php_user_filter<br />{<br />    public $stream;<br />    public string $filter;  </p>
<p>    public function onCreate(): bool {<br />        $this-&gt;stream = fopen('php://temp', 'w+');<br />        return $this-&gt;stream !== false;<br />    }  </p>
<p>    public function filter($in, $out, &amp;$consumed, $closing): int {<br />        $this-&gt;filter = 'PHP';<br />        $out_data = '';<br />        while ($bucket = stream_bucket_make_writeable($in)) {<br />            $line = explode("\n", $bucket-&gt;data);<br />            foreach ($line as $l) {<br />                if (str_contains($l, $this-&gt;filter)) {<br />                    $out_data .= $l . "\n";<br />                }<br />            }<br />        }  </p>
<p>        $bucket_out = stream_bucket_new($this-&gt;stream, $out_data);<br />        stream_bucket_append($out, $bucket_out);  </p>
<p>        return PSFS_PASS_ON;<br />    }<br />}</p>
<p>Vamos entender o que está acontecendo no código acima passo a passo.</p>
<ol>
<li>Criamos uma classe chamada <code>FilterPHP</code> que estende a classe <code>php_user_filter</code>. Esta classe é usada para criar um filtro de stream personalizado.</li>
</ol>
<p>Se você já trabalhou com classes em PHP, deve estar estranhando o fato de extender uma classe com um nome tão estranho. Isso acontece porque a classe <code>php_user_filter</code> é uma classe interna do PHP que não segue as convenções de nomenclatura de classes em PHP que foram estabelecidas pela comunidade ao longo dos anos.</p>
<ol>
<li>A classe <code>FilterPHP</code> possui duas propriedades: <code>$stream</code> e <code>$filter</code>. A propriedade <code>$stream</code> é usada para armazenar um stream temporário onde os dados filtrados serão escritos. A propriedade <code>$filter</code> é usada para armazenar o filtro que será aplicado aos dados.</li>
<li>O método <code>onCreate</code> é chamado quando o filtro é criado. Neste método, abrimos um stream temporário com <code>fopen('php://temp', 'w+')</code> e armazenamos o recurso do stream na propriedade <code>$stream</code>. Se o stream não puder ser aberto, retornamos <code>false</code>.</li>
</ol>
<blockquote>
<p>O método <code>onCreate</code> é um método obrigatório que deve ser implementado em todos os filtros de stream personalizados, funcionando como um construtor para o filtro.</p>
<p>Mas o que é <code>php://temp</code>? <code>php://temp</code> é um wrapper que permite a criação de streams temporários em PHP. Os streams temporários são armazenados na memória ou em um arquivo temporário, dependendo do tamanho dos dado</p>
</blockquote>
<p>3. O método <code>filter</code> é chamado para filtrar os dados do stream. Neste método, lemos os dados de entrada das seguintes variáveis:</p>
<ul>
<li><code>$in</code>: stream de entrada, onde os dados originais são lidos. Este stream é somente leitura, vem de <code>input</code> e é passado para o filtro.</li>
<li><code>$out</code>: stream de saída, onde os dados filtrados são escritos. Este stream é somente escrita, vem de <code>output</code> e é passado para o filtro.</li>
<li><code>&amp;$consumed</code>: quantidade de dados consumidos pelo filtro. Este valor é atualizado pelo filtro e passado de volta para o PHP.</li>
<li><code>$closing</code>: indica se o stream está sendo fechado. Se for <code>true</code>, o filtro deve liberar todos os recursos alocados.</li>
</ul>
<blockquote>
<p><strong><em>Nota*</em></strong>: O<em> `</em>&amp;<em>` </em>na frente de uma variável em PHP indica que a variável é passada por referência, ou seja, o valor da variável pode ser alterado dentro da função e refletido fora dela.*</p>
</blockquote>
<p>4. No método <code>filter</code>, lemos os dados de entrada do stream com <code>stream_bucket_make_writeable</code> e os armazenamos na variável <code>$out_data</code>. Em seguida, dividimos os dados em linhas com <code>explode("\n", $bucket-&gt;data)</code> e verificamos se cada linha contém a palavra "PHP" com <code>str_contains($l, $this-&gt;filter)</code>.</p>
<p>5. Se a linha contiver a palavra “PHP”, a linha é adicionada à variável <code>$out_data</code>. Em seguida, criamos um novo bucket de saída com <code>stream_bucket_new</code> e o adicionamos ao stream de saída com <code>stream_bucket_append</code>.</p>
<p>6. Por fim, retornamos <code>PSFS_PASS_ON</code> para indicar que o filtro deve continuar a passar os dados para o próximo filtro ou para o stream de saída.</p>
<p>Agora que criamos o filtro de stream, vamos aplicá-lo a um stream de entrada.</p>
<p>&lt;?php  </p>
<p>require_once './FilterPHP.php';  </p>
<p>$json = 'https://raw.githubusercontent.com/sschonss/stream-php/main/data.json';  </p>
<p>$fileContents = file_get_contents($json);  </p>
<p>if ($fileContents === false) {<br />    die('Failed to fetch data from the endpoint.');<br />}  </p>
<p>stream_filter_register('filterphp', 'FilterPHP') or die("Failed to register filter.");  </p>
<p>$tempStream = fopen('php://temp', 'r+');<br />fwrite($tempStream, $fileContents);<br />rewind($tempStream);  </p>
<p>$out_fp = fopen('php://stdout', 'w') or die("Failed to open output stream.");<br />stream_filter_append($tempStream, 'filterphp');  </p>
<p>while ($data = fread($tempStream, 1024)) {<br />    $data = str_replace('"name": ', '', $data);<br />    $data = str_replace('"description": ', '', $data);<br />    echo $data;<br />}  </p>
<p>fclose($tempStream);<br />fclose($out_fp);</p>
<p>Neste exemplo, estamos lendo um arquivo JSON de uma URL com <code>file_get_contents</code> e armazenando o conteúdo do arquivo em uma variável chamada <code>$fileContents</code>.</p>
<p>Em seguida, registramos o filtro de stream <code>FilterPHP</code> com <code>stream_filter_register</code>. O primeiro argumento é o nome do filtro, que será usado para aplicar o filtro ao stream de entrada. O segundo argumento é o nome da classe do filtro.</p>
<p>É importante registrar o filtro de stream antes de aplicá-lo ao stream de entrada, para que o PHP saiba como lidar com o filtro.</p>
<p>Depois, abrimos um stream temporário com <code>fopen('php://temp', 'r+')</code> e escrevemos o conteúdo do arquivo no stream com <code>fwrite</code>. Em seguida, rebobinamos o stream com <code>rewind</code>.</p>
<blockquote>
<p><strong><em>Nota*</em></strong>: O<em> `</em>rewind<em>` </em>é uma função que move o ponteiro interno do stream para o início do stream. Isso é necessário porque o ponteiro interno do stream é movido para o final do stream após a escrita.*</p>
<p><strong><em>Nota*</em></strong>: Ponteiro em PHP é como se fosse um cursor que aponta para a posição atual do stream.*</p>
</blockquote>
<p>Depois, abrimos um stream de saída com <code>fopen('php://stdout', 'w')</code> e aplicamos o filtro de stream <code>FilterPHP</code> ao stream temporário com <code>stream_filter_append</code>.</p>
<p>Por fim, lemos os dados do stream temporário com <code>fread</code> e exibimos os dados na tela com <code>echo</code>. Antes de exibir os dados, removemos as aspas duplas das chaves <code>name</code> e <code>description</code> com <code>str_replace</code>.</p>
<p>Por fim, fechamos o stream temporário e o stream de saída com <code>fclose</code>.</p>
<p>O retorno do código acima será algo como:</p>
<p>php-filter_1  |       "PHP",<br />php-filter_1  |        "PHP (Hypertext Preprocessor) é uma linguagem de script amplamente utilizada para desenvolvimento web e pode ser embutida em HTML."<br />php-filter_1  |        "Composer é um gerenciador de dependências para PHP, permitindo que você declare as bibliotecas que seu projeto depende e as instale."<br />php-filter_1  |        "Laravel é um framework web PHP, conhecido por sua sintaxe elegante e ferramentas robustas para desenvolvimento rápido de aplicativos."<br />php-filter_1  |        "Symfony é um conjunto de componentes PHP reutilizáveis e um framework web para criar aplicações e sites."<br />php-filter_1  |        "CodeIgniter é um framework PHP poderoso e simples de usar, construído para desenvolvedores que precisam de um toolkit elegante para criar aplicativos web completos."<br />php-filter_1  |       "CakePHP",<br />php-filter_1  |        "CakePHP é um framework PHP rápido de desenvolvimento que proporciona uma estrutura extensível para desenvolvedores criar aplicativos web."<br />php-filter_1  |        "Zend Framework é uma coleção de pacotes PHP profissionais com mais de 570 milhões de instalações."</p>
<p>Caso você queira testar o código acima, você pode clonar o repositório <a target="_blank" href="https://github.com/sschonss/stream-php">stream-php</a> e executar o comando <code>docker-compose up</code> para subir o container com o PHP e testar o código.</p>
<h3 id="heading-conclusao">Conclusão</h3>
<p>Streams são uma abstração poderosa e flexível para trabalhar com arquivos e outros recursos de I/O em PHP. Com streams, você pode ler e escrever dados de e para diferentes fontes, como arquivos, strings, conexões de rede, etc., de forma consistente e independente da origem ou destino dos dados.</p>
<p>Além disso, você pode usar contextos de stream para configurar streams com opções específicas, wrappers para abrir streams de diferentes tipos de recursos, funções de stream para trabalhar com streams de forma eficiente e filtros de stream para processar os dados conforme necessário.</p>
<p>Espero que este guia tenha sido útil para você entender melhor como trabalhar com streams em PHP. Se tiver alguma dúvida, sugestão ou correção, fique à vontade para entrar em contato.</p>
<p>Até a próxima!</p>
]]></content:encoded></item><item><title><![CDATA[JIT (Just in Time) — PHP]]></title><description><![CDATA[Se você ainda não atualizou para o PHP 8.0 ou maior, está na hora de fazer isso. O PHP 8.0 foi lançado em 26 de novembro de 2020 e trouxe o JIT (Just In…]]></description><link>https://schons.hashnode.dev/jit-just-in-time-php</link><guid isPermaLink="true">https://schons.hashnode.dev/jit-just-in-time-php</guid><dc:creator><![CDATA[Luiz Schons]]></dc:creator><pubDate>Tue, 28 May 2024 00:27:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586230614/ae3ea9d2-4f1f-4d88-b1ec-0ce7573ba5e1.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Se você ainda não atualizou para o PHP 8.0 ou maior, está na hora de fazer isso. O PHP 8.0 foi lançado em 26 de novembro de 2020 e trouxe o JIT (Just In Time) e é sobre ele que vamos falar hoje.</p>
<h3 id="heading-codigo-opc">Código OPC</h3>
<p>Quando você executa um script PHP, o PHP o compila para um código intermediário chamado de código OPC (PHP Opcode). O código OPC é então executado pela máquina virtual Zend.</p>
<p>Segue um exemplo de código PHP e seu código OPC:</p>
<p><strong>PHP:</strong></p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>
$a = M_PI;
$b = <span class="hljs-number">10</span> + sin($a);
<span class="hljs-keyword">echo</span> <span class="hljs-string">"Result: "</span>, $b;
</code></pre>
<p><strong>Código OPC:</strong></p>
<pre><code class="lang-c">Finding entry points
Branch analysis from position: <span class="hljs-number">0</span>
<span class="hljs-number">1</span> jumps found. (Code = <span class="hljs-number">62</span>) Position <span class="hljs-number">1</span> = <span class="hljs-number">-2</span>
filename:       /in/ceTlX
function name:  (null)
number of ops:  <span class="hljs-number">9</span>
compiled vars:  !<span class="hljs-number">0</span> = $a, !<span class="hljs-number">1</span> = $b
line      #* E I O op                           fetch          ext  <span class="hljs-keyword">return</span>  operands
-------------------------------------------------------------------------------------
    <span class="hljs-number">2</span>     <span class="hljs-number">0</span>  E &gt;   ASSIGN                                                   !<span class="hljs-number">0</span>, <span class="hljs-number">3.14159</span>
    <span class="hljs-number">3</span>     <span class="hljs-number">1</span>        INIT_FCALL                                               <span class="hljs-string">'sin'</span>
          <span class="hljs-number">2</span>        SEND_VAR                                                 !<span class="hljs-number">0</span>
          <span class="hljs-number">3</span>        DO_ICALL                                         $<span class="hljs-number">3</span>
          <span class="hljs-number">4</span>        ADD                                              ~<span class="hljs-number">4</span>      <span class="hljs-number">10</span>, $<span class="hljs-number">3</span>
          <span class="hljs-number">5</span>        ASSIGN                                                   !<span class="hljs-number">1</span>, ~<span class="hljs-number">4</span>
    <span class="hljs-number">4</span>     <span class="hljs-number">6</span>        ECHO                                                     <span class="hljs-string">'Result%3A+'</span>
          <span class="hljs-number">7</span>        ECHO                                                     !<span class="hljs-number">1</span>
    <span class="hljs-number">5</span>     <span class="hljs-number">8</span>      &gt; RETURN                                                   <span class="hljs-number">1</span>
</code></pre>
<p>Basicamente, o código OPC é uma sequência de instruções que a máquina virtual Zend executa.</p>
<h1 id="heading-jit"><strong>JIT</strong></h1>
<p>Mas onde o JIT entra nisso? O JIT é um compilador que compila o código OPC em código de máquina nativo. Isso significa que o JIT compila o código OPC em código de máquina que a CPU pode executar diretamente.</p>
<p>Dessa forma não usamos mais a máquina virtual Zend para executar o código OPC, mas sim o código de máquina nativo gerado pelo JIT.</p>
<h1 id="heading-habilitando-o-jit"><strong>Habilitando o JIT</strong></h1>
<p>O JIT não é habilitado por padrão. Para habilitá-lo, você precisa de alguns passos adicionais.</p>
<p>Primeiro, você precisa instalar a extensão <code>opcache</code>:</p>
<pre><code class="lang-bash">sudo apt-get install php8.0-opcache
</code></pre>
<p>Depois, você precisa habilitar o JIT no arquivo de configuração do PHP (<code>php.ini</code>):</p>
<pre><code class="lang-php">[opcache]
opcache.enable=<span class="hljs-number">1</span>
opcache.jit_buffer_size=<span class="hljs-number">100</span>M
opcache.jit=tracing
opcache.jit=<span class="hljs-number">1205</span>
opcache.enable_cli=<span class="hljs-number">1</span>
</code></pre>
<p>Aqui, estamos habilitando o JIT e definindo o tamanho do buffer JIT para 100 MB. O JIT pode operar em dois modos: <code>tracing</code> e <code>function</code>. O modo <code>tracing</code> é o modo padrão e é mais rápido, mas o modo <code>function</code> é mais preciso.</p>
<p>Para entender melhor a diferença entre os modos <code>tracing</code> e <code>function</code>, recomendo a leitura da <a target="_blank" href="https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.jit">documentação oficial</a>.</p>
<p>E depois habilitamos o JIT para a linha de comando (<code>opcache.enable_cli=1</code>).  </p>
<h2 id="heading-benchmark"><strong>Benchmark</strong></h2>
<p>Se você roda o PHP na Web, o JIT pode não fazer muita diferença. Mas se você roda scripts PHP na linha de comando, o JIT pode trazer um grande ganho de performance.</p>
<p>Pois então, vamos fazer um benchmark para comparar a performance do PHP 8.0 com e sem JIT.</p>
<p>No repositório <a target="_blank" href="https://github.com/sschonss/jit-php-doc">php-jit-doc</a> você encontra um script PHP que calcula um monte de coisa para deixar o processador bem ocupado.</p>
<blockquote>
<p><em>Não vamos entrar em detalhes de escrita e padrões deste script, pois o foco é o JIT.</em></p>
</blockquote>
<p>Vamos rodar o script sem JIT:</p>
<pre><code class="lang-bash">./start.sh
</code></pre>
<p>O resultado foi:</p>
<pre><code class="lang-bash">Execution time: 6.2343468666077 seconds
Encrypted: 46d37684d6032ef0167c00d29f3358d5a180438d3e8604e4c11728699d78fdb0a32284bfa8234ed59f69e7febd0d23bebab983211565582a98da8cb5c8800e42
</code></pre>
<p>Agora vamos habilitar o JIT e rodar o script novamente:</p>
<pre><code class="lang-bash">./start-jit.sh
</code></pre>
<p>O resultado foi:</p>
<pre><code class="lang-bash">Execution time: 3.292811870575 seconds
Encrypted: 46d37684d6032ef0167c00d29f3358d5a180438d3e8604e4c11728699d78fdb0a32284bfa8234ed59f69e7febd0d23bebab983211565582a98da8cb5c8800e42
</code></pre>
<p>Com o JIT habilitado, o script PHP rodou 3 segundos mais rápido. Isso é um ganho de performance muito considerável.</p>
<p>Mas lembre-se, o JIT não é uma bala de prata. Ele pode não fazer muita diferença para scripts PHP que rodam na Web. Mas para scripts PHP que rodam na linha de comando, o JIT pode trazer um grande ganho de performance.</p>
<h1 id="heading-conclusao"><strong>Conclusão</strong></h1>
<p>Esse foi um breve resumo sobre o JIT no PHP 8.0. Se você quiser saber mais sobre o JIT, recomendo a leitura da <a target="_blank" href="https://www.php.net/manual/en/intro.opcache.php">documentação oficial</a></p>
<p>Trouxe somente exemplos básicos e didáticos para que você entenda o que é o JIT e como habilitá-lo. Acho que era importante trazer esse conteúdo para o prefil para que possamos falar sobre assuntos mais complexos e avançados.</p>
<p>Se você gostou do conteúdo, deixe um comentário e compartilhe com seus amigos. Até a próxima!</p>
]]></content:encoded></item><item><title><![CDATA[Programação Paralela, e Assíncrona com PHP]]></title><description><![CDATA[Photo by Ben Griffiths on Unsplash PHP é uma linguagem de programação de propósito geral, amplamente utilizada para desenvolvimento web. No entanto, PHP…]]></description><link>https://schons.hashnode.dev/programac3a7c3a3o-paralela-e-assc3adncrona-com-php-5969c49d0bba</link><guid isPermaLink="true">https://schons.hashnode.dev/programac3a7c3a3o-paralela-e-assc3adncrona-com-php-5969c49d0bba</guid><dc:creator><![CDATA[Luiz Schons]]></dc:creator><pubDate>Thu, 25 Apr 2024 00:20:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586238526/ea059127-9847-4d0f-87aa-f65a9a93723c.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Photo by <a target="_blank" href="https://unsplash.com/@benofthenorth?utm_source=medium&amp;utm_medium=referral">Ben Griffiths</a> on <a target="_blank" href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p>
<h3 id="heading-introducao">Introdução</h3>
<p>PHP é uma linguagem de programação de propósito geral, amplamente utilizada para desenvolvimento web. No entanto, PHP também pode ser utilizado para desenvolvimento de aplicações desktop, scripts de automação, entre outros. Neste artigo, vamos explorar o uso de PHP para programação paralela e assíncrona.</p>
<h3 id="heading-programacao-paralela">Programação Paralela</h3>
<p>Programação paralela é um paradigma de programação que consiste em dividir um problema em partes menores que podem ser executadas simultaneamente. Em PHP, a programação paralela pode ser feita utilizando a extensão <a target="_blank" href="https://www.php.net/manual/en/book.parallel.php">parallel</a> ou a extensão <a target="_blank" href="https://www.php.net/manual/en/book.pthreads.php">pthreads</a>, que permite a criação de threads em PHP.</p>
<h4 id="heading-mas-voce-sabe-o-que-e-uma-thread">Mas você sabe o que é uma thread?</h4>
<blockquote>
<p><em>Uma thread é uma unidade básica de processamento que pode ser executada de forma independente. Em um programa paralelo, várias threads podem ser executadas simultaneamente, o que pode resultar em um aumento significativo de desempenho.</em></p>
<p><em>Quando você compra um processador com múltiplos núcleos, você está comprando a capacidade de executar várias threads simultaneamente. No entanto, para aproveitar ao máximo essa capacidade, é necessário que o software seja desenvolvido de forma a utilizar esses recursos de forma eficiente.</em></p>
</blockquote>
<h4 id="heading-exemplo-de-programacao-paralela-em-php">Exemplo de Programação Paralela em PHP</h4>
<p>&lt;?php  </p>
<p>use function parallel\run;  </p>
<p>class DataProcessor<br />{<br />    public function process(array $data): array {<br />        $formattedData = $this-&gt;formatData($data);<br />        $excel_file = $this-&gt;generateExcel($formattedData);<br />        $this-&gt;sendEmail($excel_file, Auth::user()-&gt;email);<br />    }<br />}  </p>
<p>$data = range(1, 1000);  </p>
<p>$processor = new DataProcessor();  </p>
<p>$parallelResult1 = run(function () use ($processor, $data) {<br />    return $processor-&gt;process($data);<br />});  </p>
<p>$parallelResult2 = $processor-&gt;process($data);</p>
<p>No exemplo acima, estamos utilizando a extensão parallel para executar o método <code>process</code> da classe <code>DataProcessor</code> em paralelo. Isso significa que o método será executado em uma thread separada, o que pode resultar em um aumento de desempenho.</p>
<p>Isso não é bala de prata, e nem sempre é a melhor solução para todos os problemas. No entanto, em alguns casos, a programação paralela pode ser uma forma eficiente de melhorar o desempenho de uma aplicação.</p>
<p>O exemplo acima é bastante simplificado, mas ilustra como a programação paralela pode ser utilizada em PHP. Para obter mais informações sobre a extensão parallel, consulte a <a target="_blank" href="https://www.php.net/manual/en/book.parallel.php">documentação oficial</a>.</p>
<p>Aqui esta uma imagem de um exemplo de programação paralela em PHP:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586235019/e7563efd-e150-4fc2-a5d1-758f6bb59a65.png" alt /></p>
<p><em>Imagem ilustrativa de um exemplo de programação paralela retirada da internet.</em></p>
<p>Perceba que a programação paralela é diferente da programação assíncrona. Na programação paralela, várias threads são executadas simultaneamente, enquanto na programação assíncrona, várias tarefas podem ser executadas de forma concorrente, mas não necessariamente simultaneamente.</p>
<h3 id="heading-programacao-assincrona">Programação Assíncrona</h3>
<p>Programação assíncrona é um paradigma de programação que consiste em executar tarefas de forma concorrente, sem a necessidade de esperar que uma tarefa seja concluída para iniciar outra. Em PHP, a programação assíncrona pode ser feita utilizando a extensão <a target="_blank" href="https://www.swoole.co.uk/">swoole</a>, que permite a criação de servidores web assíncronos em PHP.</p>
<h4 id="heading-mas-voce-sabe-o-que-e-um-servidor-web-assincrono">Mas você sabe o que é um servidor web assíncrono?</h4>
<blockquote>
<p><em>Um servidor web assíncrono é um servidor que pode lidar com várias requisições simultaneamente, sem a necessidade de criar uma nova thread para cada requisição. Isso permite que o servidor seja mais eficiente e possa lidar com um grande número de requisições de forma concorrente.</em></p>
<p><em>Você pode achar o termo “servidor auto-contido” em alguns lugares, mas a ideia é a mesma: um servidor que pode lidar com várias requisições simultaneamente, sem a necessidade de criar uma nova thread para cada requisição.</em></p>
</blockquote>
<h4 id="heading-exemplo-de-programacao-assincrona-em-php">Exemplo de Programação Assíncrona em PHP</h4>
<p>use Swoole\Http\Server;  </p>
<p>$server = new Server("0.0.0.0", 9501);  </p>
<p>$server-&gt;on("start", function (Server $server) {<br />    echo "Server started at http://{$server-&gt;host}:{$server-&gt;port}\n";<br />});  </p>
<p>$server-&gt;on("request", function ($request, $response) {<br />    $data = $request-&gt;rawContent();<br />    $response-&gt;end("Received data: $data");<br />});  </p>
<p>$server-&gt;start();</p>
<p>No exemplo acima, estamos utilizando a extensão swoole para criar um servidor web assíncrono em PHP. O servidor irá escutar na porta 9501 e responder a todas as requisições com uma mensagem contendo os dados recebidos</p>
<p>Esse exemplo é bastante simplificado, mas ilustra como a programação assíncrona pode ser utilizada em PHP. Para obter mais informações sobre a extensão swoole, consulte a <a target="_blank" href="https://www.swoole.co.uk/">documentação oficial</a>.</p>
<p>Aqui esta uma imagem de um exemplo de programação assíncrona:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586236219/554adf54-7f19-4abf-bea2-aad9a2b23333.png" alt /></p>
<p>Imagem ilustrativa de um exemplo de programação assíncrona retirada da internet.</p>
<h3 id="heading-ciclo-de-vida-de-uma-requisicao-no-php-fpm">Ciclo de Vida de uma Requisição no PHP-FPM</h3>
<p>Quando uma requisição é feita a um servidor web PHP, como o PHP-FPM, o servidor passa por várias etapas para processar a requisição e retornar uma resposta ao cliente. O ciclo de vida de uma requisição no PHP-FPM pode ser dividido em várias etapas:</p>
<ol>
<li><strong>Recebimento da Requisição:</strong> O servidor web (Apache, Nginx, etc.) recebe a requisição do cliente e a encaminha para o PHP-FPM.</li>
<li><strong>Pool de Processos:</strong> O PHP-FPM mantém um pool de processos PHP pré-carregados para processar as requisições. Quando uma requisição é recebida, o PHP-FPM escolhe um processo disponível para processar a requisição.</li>
<li><strong>Autoload:</strong> O PHP-FPM carrega os arquivos necessários para processar a requisição, incluindo as classes e funções utilizadas no script PHP. As classes e funções são carregadas automaticamente pelo PHP, de acordo com as regras de autoload definidas no arquivo <code>composer.json</code>.</li>
<li><strong>Análise e Compilação:</strong> O PHP analisa o script PHP e compila o código em uma forma que pode ser executada pelo interpretador PHP.</li>
<li><strong>Execução do Script:</strong> O PHP executa o script PHP, processando as instruções e gerando a saída da requisição.</li>
<li><strong>Gerar Resposta:</strong> O PHP-FPM gera a resposta da requisição, incluindo o cabeçalho HTTP e o corpo da resposta.</li>
<li><strong>Envio da Resposta:</strong> O PHP-FPM envia a resposta ao servidor web, que a encaminha de volta ao cliente.</li>
<li><strong>Encerramento do Processo:</strong> Após enviar a resposta, o processo PHP é encerrado e retorna ao pool de processos disponíveis para processar novas requisições.</li>
</ol>
<p>O ciclo de vida de uma requisição no PHP-FPM pode variar dependendo da configuração do servidor web e do PHP-FPM. No entanto, essas etapas são comuns na maioria dos servidores web PHP.</p>
<h3 id="heading-ciclo-de-vida-de-uma-requisicao-no-swoole">Ciclo de Vida de uma Requisição no Swoole</h3>
<p>Quando uma requisição é feita a um servidor web assíncrono PHP, como o Swoole, o servidor passa por várias etapas para processar a requisição e retornar uma resposta ao cliente. O ciclo de vida de uma requisição no Swoole pode ser dividido em várias etapas:</p>
<p>O início do ciclo de vida de uma requisição no Swoole começa com a inicialização do servidor Swoole e a configuração do script PHP que será executado para processar as requisições. O servidor Swoole escuta em uma porta específica e aguarda a chegada de requisições.</p>
<p>Nesse ponto, o servidor já está com o autoload configurado, as classes e funções necessárias já estão carregadas e o script PHP está pronto para processar as requisições.</p>
<ol>
<li><strong>Recebimento da Requisição:</strong> Quando uma requisição é feita ao servidor Swoole, o servidor recebe a requisição e a encaminha para o script PHP configurado para processar a requisição.</li>
<li><strong>Processamento da Requisição:</strong> O script PHP processa a requisição, executando as instruções necessárias para gerar a resposta da requisição.</li>
<li><strong>Análise e Compilação:</strong> O PHP analisa o script PHP e compila o código em uma forma que pode ser executada pelo interpretador PHP.</li>
<li><strong>Execução do Script:</strong> O PHP executa o script PHP, processando as instruções e gerando a saída da requisição.</li>
<li><strong>Gerar Resposta:</strong> O script PHP gera a resposta da requisição, incluindo o cabeçalho HTTP e o corpo da resposta.</li>
<li><strong>Envio da Resposta:</strong> O servidor Swoole envia a resposta ao cliente, encerrando o ciclo de vida da requisição.</li>
</ol>
<p>E nesse momento, o servidor Swoole está pronto para processar novas requisições, mantendo um pool de processos PHP disponíveis para processar as requisições de forma assíncrona.</p>
<p>A diferença entre o ciclo de vida de uma requisição no PHP-FPM e no Swoole está na forma como o servidor PHP é inicializado e como as requisições são processadas. No PHP-FPM, o servidor PHP é inicializado para processar uma única requisição de cada vez, enquanto no Swoole, o servidor PHP é inicializado para processar várias requisições de forma assíncrona.</p>
<h3 id="heading-conclusao">Conclusão</h3>
<p>Neste artigo, exploramos o uso de PHP para programação paralela e assíncrona. A programação paralela e assíncrona são técnicas de programação que podem ser utilizadas para melhorar o desempenho de uma aplicação e lidar com um grande número de requisições de forma eficiente.</p>
<p>Nem tudo é regra, e nem sempre a programação paralela ou assíncrona é a melhor solução para um problema. No entanto, é sempre bom conhecer as diferentes técnicas de programação disponíveis e saber quando utilizá-las de forma eficiente.</p>
<p>Espero que este artigo tenha sido útil e que você tenha aprendido algo novo sobre programação paralela e assíncrona em PHP. Espero que futuramente eu consiga fazer um artigo mais detalhado sobre cada uma dessas técnicas, com exemplos mais complexos e detalhados.</p>
<p>Qualquer dúvida, correção ou sugestão, fique à vontade para entrar em contato. Obrigado por ler até aqui!</p>
<h3 id="heading-referencias">Referências</h3>
<ul>
<li><a target="_blank" href="https://www.php.net/manual/en/book.parallel.php">PHP Parallel Extension</a></li>
<li><a target="_blank" href="https://www.php.net/manual/en/book.pthreads.php">PHP pthreads Extension</a></li>
<li><a target="_blank" href="https://www.swoole.co.uk/">Swoole Extension</a></li>
<li><a target="_blank" href="https://www.php.net/manual/en/install.fpm.php">PHP-FPM</a></li>
<li><a target="_blank" href="https://www.php.net/manual/en/language.oop5.autoload.php">PHP Autoload</a></li>
<li><a target="_blank" href="https://getcomposer.org/">PHP Composer</a></li>
<li><a target="_blank" href="https://www.php-fig.org/psr/">PHP PSR</a></li>
<li><a target="_blank" href="https://fastcgi-archives.github.io/">FastCGI</a></li>
</ul>
<h3 id="heading-autor">Autor</h3>
<ul>
<li><a target="_blank" href="https://linkedin.com/in/luiz-schons">Luiz Schons</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Tabelas Hash]]></title><description><![CDATA[Uma tabela hash é uma estrutura de dados que associa chaves de pesquisa a valores. Sua principal função é, a partir de uma chave simples, fazer uma busca…]]></description><link>https://schons.hashnode.dev/tabelas-hash-1f1a85a83795</link><guid isPermaLink="true">https://schons.hashnode.dev/tabelas-hash-1f1a85a83795</guid><dc:creator><![CDATA[Luiz Schons]]></dc:creator><pubDate>Wed, 06 Mar 2024 22:52:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586243421/155936fd-0213-47ef-bc8a-168f55e0d62c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Uma tabela hash é uma estrutura de dados que associa chaves de pesquisa a valores.</p>
<p>Sua principal função é, a partir de uma chave simples, fazer uma busca rápida e eficiente para encontrar o valor desejado.</p>
<h3 id="heading-1-funcoes-hash">1. Funções Hash</h3>
<h3 id="heading-11-introducao">1.1. Introdução</h3>
<p>A função hash é uma função que, a partir de uma entrada de dados, gera um valor numérico que identifica a posição de um elemento em uma tabela hash.</p>
<p>Nós já vimos algumas outras estruturas de dados aqui nesse perfil, como buscar um elemento em uma lista encadeada ou em uma árvore binária, e vimos que a complexidade dessas operações é O(n) e O(log n), respectivamente.</p>
<p>A tabela hash, por sua vez, consegue fazer essa busca em O(1), ou seja, em tempo constante.</p>
<p>Isso significa que, não importa o tamanho da tabela, a busca será feita no mesmo tempo.</p>
<h3 id="heading-12-exemplo">1.2. Exemplo</h3>
<p>Muitas vezes, a função hash é usada para criar um índice para um array, e é por isso que a função hash deve ser rápida e eficiente.</p>
<p>Desenvolvi uma função hash muito simples, que pega uma palavra e retorna um número que representa essa palavra.</p>
<p>package main  </p>
<p>import (<br /> "fmt"<br />)  </p>
<p>func main() {<br /> for {<br />  run()<br /> }<br />}  </p>
<p>func run(){<br /> array_palavras := [10000]string{}<br /> fmt.Println("Digite uma palavra para ser hasheada")<br /> var palavra string<br /> fmt.Scanln(&amp;palavra)<br /> hash_palavra := fake_hash(palavra)<br /> fmt.Println("A palavra", palavra, "tem o hash", hash_palavra)<br /> array_palavras[hash_palavra] = palavra<br /> fmt.Println("A palavra", palavra, "foi adicionada ao array na posicao", hash_palavra)<br /> fmt.Println("Digite um hash para buscar a palavra correspondente")<br /> var hash int<br /> fmt.Scanln(&amp;hash)<br /> fmt.Println("A palavra correspondente ao hash", hash, "é", array_palavras[hash])<br />}  </p>
<p>func fake_hash(palavra string) int {<br /> hash := 0<br /> for i := 0; i &lt; len(palavra); i++ {<br />  hash += int(palavra[i])<br /> }<br /> return hash<br />}</p>
<p>Nesse exemplo, a função fake_hash pega a palavra e soma os valores ASCII de cada caractere, retornando um número que representa a palavra.</p>
<p>Mas fique tranquilo, provavelmente você não vai precisar criar uma função hash, pois as linguagens de programação já possuem funções hash prontas e otimizadas.</p>
<p>Pode ser que na sua linguagem de programação a função hash seja chamada de outra coisa, como “map” ou “dicionário”, mas o conceito é o mesmo.</p>
<h3 id="heading-2-usabilidade">2. Usabilidade</h3>
<h3 id="heading-21-lista-telefonica">2.1. Lista Telefônica</h3>
<p>Um exemplo clássico de uso de tabela hash é a lista telefônica.</p>
<p>Imagine que você quer encontrar o número de telefone de uma pessoa.</p>
<p>Se a lista telefônica fosse uma lista encadeada, você teria que percorrer toda a lista até encontrar o nome da pessoa.</p>
<p>Se fosse uma árvore binária, você teria que percorrer a árvore até encontrar o nome da pessoa.</p>
<p>Mas, como a lista telefônica é uma tabela hash, você pode encontrar o número de telefone de uma pessoa em tempo constante, ou seja, em O(1).</p>
<p>Vamos entender mais sobre isso.</p>
<p>Ao adicionar um nome e um número de telefone à lista telefônica, a função hash é usada para gerar um índice para esse nome.</p>
<p>Quando você quer encontrar o número de telefone de uma pessoa, a função hash é usada para gerar o índice correspondente ao nome da pessoa, e o número de telefone é retornado.</p>
<p>Dessa forma:</p>
<p>package main  </p>
<p>import (<br />    "fmt"<br />)<br />func main() {<br />    lista_telefonica := make(map[string]string)<br />    lista_telefonica["João"] = "1234-5678"<br />    lista_telefonica["Maria"] = "8765-4321"<br />    lista_telefonica["José"] = "4321-5678"<br />    lista_telefonica["Ana"] = "5678-4321"<br />    fmt.Println("O número de telefone de João é", lista_telefonica["João"])<br />    fmt.Println("O número de telefone de Maria é", lista_telefonica["Maria"])<br />    fmt.Println("O número de telefone de José é", lista_telefonica["José"])<br />    fmt.Println("O número de telefone de Ana é", lista_telefonica["Ana"])<br />}</p>
<p>Nesse exemplo, a função hash é usada para gerar um índice para cada nome, e o número de telefone é retornado em tempo constante.</p>
<h3 id="heading-22-dns">2.2. DNS</h3>
<p>Outro exemplo clássico de uso de tabela hash é o DNS (Domain Name System).</p>
<p>O DNS é um sistema que traduz nomes de domínio da Internet em endereços IP.</p>
<p>Imagine que você quer acessar um site, como <a target="_blank" href="http://www.google.com/">www.google.com</a>.</p>
<p>Se o DNS não fosse uma tabela hash, você teria que percorrer toda a lista de domínios até encontrar o nome do site, imagina o tempo que isso levaria.</p>
<p>Mas, como o DNS é uma tabela hash, você pode encontrar o endereço IP de um site em tempo constante, ou seja, em O(1).</p>
<p>Vamos entender mais sobre isso com um exemplo.</p>
<p>package main  </p>
<p>import (<br />    "fmt"<br />)<br />func main() {<br />    dns := make(map[string]string)<br />    dns["www.google.com"] = "192.168.5.5"<br />    dns["www.facebook.com"] = "192.168.40.21"<br />    dns["www.twitter.com"] = "192.168.11.11"<br />    fmt.Println("O endereço IP de www.google.com é", dns["www.google.com"])<br />    fmt.Println("O endereço IP de www.facebook.com é", dns["www.facebook.com"])<br />    fmt.Println("O endereço IP de www.twitter.com é", dns["www.twitter.com"])<br />}</p>
<p>Entende como são exemplos bem similares? A ideia é a mesma, a tabela hash é usada para gerar um índice para cada nome, e o endereço IP é retornado em tempo constante.</p>
<h3 id="heading-3-colisoes">3. Colisões</h3>
<h3 id="heading-31-introducao">3.1. Introdução</h3>
<p>Uma colisão ocorre quando duas chaves de pesquisa diferentes têm o mesmo valor hash.</p>
<p>Mas isso é possível? Como a função hash gera um valor único para cada chave de pesquisa?</p>
<p>Bom, a função hash não gera um valor único para cada chave de pesquisa, ela gera um valor que representa a chave de pesquisa.</p>
<p>E, como o número de chaves de pesquisa é muito maior do que o número de valores hash, é possível que duas chaves de pesquisa diferentes tenham o mesmo valor hash.</p>
<h3 id="heading-32-tratamento-de-colisoes">3.2. Tratamento de Colisões</h3>
<p>Existem várias maneiras de tratar colisões, mas as duas mais comuns são:</p>
<ul>
<li>Encadeamento</li>
<li>Endereçamento Aberto</li>
</ul>
<h4 id="heading-321-encadeamento">3.2.1. Encadeamento</h4>
<p>No encadeamento, cada entrada da tabela hash é uma lista encadeada.</p>
<p>Quando ocorre uma colisão, a chave de pesquisa é adicionada à lista encadeada correspondente.</p>
<p>Isso significa que, se duas chaves de pesquisa diferentes têm o mesmo valor hash, elas são adicionadas à mesma lista encadeada.</p>
<p>A principal vantagem do encadeamento é que ele é simples de implementar, mas a principal desvantagem é que ele pode ser ineficiente em termos de espaço e desempenho.</p>
<h4 id="heading-322-enderecamento-aberto">3.2.2. Endereçamento Aberto</h4>
<p>No endereçamento aberto, quando ocorre uma colisão, a chave de pesquisa é adicionada a outra posição da tabela hash.</p>
<p>Isso significa que, se duas chaves de pesquisa diferentes têm o mesmo valor hash, a segunda chave de pesquisa é adicionada a outra posição da tabela hash.</p>
<p>A principal vantagem do endereçamento aberto é que ele é eficiente em termos de espaço e desempenho, mas a principal desvantagem é que ele é mais complexo de implementar.</p>
<h3 id="heading-4-desempenho">4. Desempenho</h3>
<h3 id="heading-41-fator-de-carga">4.1. Fator de Carga</h3>
<p>O fator de carga é a razão entre o número de chaves de pesquisa e o número de posições da tabela hash.</p>
<p>Quanto maior o fator de carga, maior a probabilidade de colisões.</p>
<p>Por isso, é importante manter o fator de carga baixo, para garantir um bom desempenho da tabela hash.</p>
<p>Um fator de carga ideal é menor que 0,7, ou seja, menos de 70% das posições da tabela hash estão ocupadas.</p>
<h4 id="heading-411-como-calcular-o-fator-de-carga">4.1.1. Como Calcular o Fator de Carga</h4>
<p>O fator de carga é calculado da seguinte forma:</p>
<p>NK = número de chaves de pesquisa<br />NP = número de posições da tabela hash</p>
<p>NK / NP = fator de carga</p>
<p>Por exemplo, se a tabela hash tem 100 posições e 70 chaves de pesquisa, o fator de carga é 0,7.</p>
<p>70 / 100 = 0,7</p>
<h3 id="heading-42-redimensionamento">4.2. Redimensionamento</h3>
<p>Quando o fator de carga é maior que 0,7, é necessário redimensionar a tabela hash.</p>
<p>O redimensionamento da tabela hash é feito da seguinte forma:</p>
<ul>
<li>Crie uma nova tabela hash com o dobro do tamanho da tabela hash original</li>
<li>Adicione todas as chaves de pesquisa da tabela hash original à nova tabela hash</li>
<li>Descarte a tabela hash original</li>
</ul>
<p>O redimensionamento da tabela hash é uma operação custosa, mas é necessária para garantir um bom desempenho da tabela hash.</p>
<h3 id="heading-5-sha">5. SHA</h3>
<p>SHA (Secure Hash Algorithm) é uma família de funções hash criptográficas.</p>
<p>Ela é uma ótima função hash, pois gera um valor único para cada chave de pesquisa.</p>
<p>SHA é amplamente utilizada em criptografia, segurança da informação e autenticação.</p>
<p>SHA é uma função hash muito segura, e é praticamente impossível encontrar duas chaves de pesquisa diferentes com o mesmo valor hash.</p>
<p>Uma das principais vantagens de SHA é que ela é rápida, eficiente e unidirecional, ou seja, é fácil calcular o valor hash de uma chave de pesquisa, mas é praticamente impossível calcular a chave de pesquisa a partir do valor hash.</p>
<p>Segue um exemplo de como usar SHA em Go:</p>
<p>package main<br />import (<br />    "crypto/sha256"<br />    "fmt"<br />)  </p>
<p>func main() {<br />    palavra := "hello"<br />    hash := sha256.Sum256([]byte(palavra))<br />    fmt.Printf("O hash de %s é %x\n", palavra, hash)<br />}</p>
<p>Nesse exemplo, a função hash SHA-256 é usada para gerar um valor hash para a palavra “hello”.</p>
<p>Mas se você quiser usar o SHA-256 para gerar a palavra “hello” a partir do valor hash, você não vai conseguir.</p>
<h3 id="heading-6-conclusao">6. Conclusão</h3>
<p>As tabelas hash são estruturas de dados muito eficientes para fazer buscas rápidas e eficientes.</p>
<p>Elas são amplamente utilizadas em aplicações do mundo real, como listas telefônicas, DNS, criptografia e segurança da informação.</p>
<p>Você aprendeu sobre funções hash, usabilidade, colisões, desempenho e SHA.</p>
<p>Nunca implemente uma função hash e use em produção, sempre use funções hash prontas e otimizadas da sua linguagem de programação.</p>
<p>Espero que você tenha aprendido bastante sobre tabelas hash, e que você possa aplicar esse conhecimento em suas aplicações do mundo real.</p>
<p>Se você tiver alguma dúvida ou sugestão, deixe nos comentários.</p>
<p>Até a próxima!</p>
]]></content:encoded></item><item><title><![CDATA[Quicksort]]></title><description><![CDATA[Quicksort é um algoritmo de ordenação muito eficiente, inventado por C.A.R. Hoare em 1960. Ele é amplamente utilizado para ordenação de arrays. O algoritmo…]]></description><link>https://schons.hashnode.dev/quicksort-33f8e917ab6c</link><guid isPermaLink="true">https://schons.hashnode.dev/quicksort-33f8e917ab6c</guid><dc:creator><![CDATA[Luiz Schons]]></dc:creator><pubDate>Wed, 28 Feb 2024 00:09:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586112430/07eb835b-da5f-4436-b39b-a5b2fcc4c5c7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Quicksort é um algoritmo de ordenação muito eficiente, inventado por C.A.R. Hoare em 1960. Ele é amplamente utilizado para ordenação de arrays. O algoritmo que apresentamos a seguir é uma versão recursiva do Quicksort que seleciona um elemento como pivô e particiona o array de forma que todos os elementos menores que o pivô fiquem antes dele e os maiores fiquem depois. Os subarrays são então ordenados recursivamente.</p>
<p>Mas antes de começarmos a falar sobre o algoritmo, você precisa entender o que são métodos de DC (Divisão e Conquista).</p>
<h3 id="heading-metodos-de-dc">Métodos de DC</h3>
<p>Métodos de DC são algoritmos que resolvem um problema dividindo-o em subproblemas, resolvendo os subproblemas recursivamente e combinando as soluções dos subproblemas para resolver o problema original.</p>
<p>Vamos ver um exemplo de um algoritmo que utiliza DC.</p>
<h3 id="heading-exemplo">Exemplo</h3>
<p>Imagina que você tenha um array de inteiros e quer saber a soma de todos os elementos desse array. Você pode resolver esse problema utilizando DC da seguinte forma:</p>
<p>Array: [1, 2, 3, 4, 5]</p>
<p>Qual é o caso base? O caso base é quando o array tem apenas um elemento. Nesse caso, a soma é o próprio elemento.</p>
<p>Como iremos chegar no caso base? Vamos remover um elemento e ver o que sobra.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586090466/a10290d3-4010-4716-8fc4-b65d59558f23.png" alt /></p>
<p>Agora que chegamos no caso base, vamos resolver o problema. A soma de todos os elementos do array [5] é 5. Agora, vamos somar o 4 que removemos anteriormente. A soma de todos os elementos do array [4, 5] é 9. Agora, vamos somar o 3 que removemos anteriormente. A soma de todos os elementos do array [3, 9] é 12. E assim por diante.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586091937/f05822b2-a2fd-4bf6-aea3-e2049ad244b4.png" alt /></p>
<p>A soma de todos os elementos do array [1, 2, 3, 4, 5] é 15.</p>
<p>Esse é um caso simples de DC e muitos algoritmos utilizam esse conceito para resolver problemas.</p>
<h3 id="heading-por-que-nao-usar-um-loop">Por que não usar um loop?</h3>
<p>Você pode estar se perguntando por que não usar um loop para resolver esse problema. E a resposta é: você pode! Mas, em alguns casos, a solução utilizando DC é mais simples e mais fácil de entender.</p>
<p>Outro ponto importante é que, em algumas linguagens, principalmente as funcionais, não existe a estrutura de repetição (for, while, etc). Então, a única forma de resolver um problema é utilizando DC.</p>
<p>Entendendo esse algoritimo, você consegue trabalhar com outras linguagens de programação e entender como elas funcionam.</p>
<p>Caso tenha alguma dúvida, busque por mais exemplos e tente resolver problemas utilizando DC.</p>
<h3 id="heading-quicksort">Quicksort</h3>
<p>Agora que você entendeu o que é DC, vamos falar sobre o Quicksort.</p>
<p>O Quicksort é um algoritmo de ordenação muito eficiente, inventado por C.A.R. Hoare em 1960. Ele é amplamente utilizado para ordenação de arrays. O algoritmo que apresentamos a seguir é uma versão recursiva do Quicksort que seleciona um elemento como pivô e particiona o array de forma que todos os elementos menores que o pivô fiquem antes dele e os maiores fiquem depois. Os subarrays são então ordenados recursivamente.</p>
<h3 id="heading-exemplo-1">Exemplo</h3>
<p>Vamos ver um exemplo de como o Quicksort funciona.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586094051/5195328a-abf0-4d07-be67-675cd27dfc0d.png" alt /></p>
<p>Passo 1: Escolha um elemento como pivô. Vamos escolher o 6.</p>
<p>Passo 2: Particione o array de forma que todos os elementos menores que o pivô fiquem antes dele e os maiores fiquem depois.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586095573/5c2bacf4-226e-47d9-bf60-144eea312205.png" alt /></p>
<p>Passo 3: Ordenar os subarrays recursivamente.</p>
<p>O que significa ordenar os subarrays recursivamente? Significa que vamos repetir os passos 1 e 2 para os subarrays, escolhendo um pivô e particionando o array.</p>
<p>Vamos escolher o 1 como pivô.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586097406/11e39580-eeee-46e7-84a2-fac3b07437e7.png" alt /></p>
<p>Vamos escolher o 2 como pivô.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586099064/22e0a07e-fcb2-4d68-917c-5d84a92b907d.png" alt /></p>
<p>Agora somos obrigados a escolher o 3 como pivô.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586100502/7bcf8627-69bb-4cf2-921f-c820c064fe67.png" alt /></p>
<p>Agora que chegamos no caso base, vamos ordenar os subarrays recursivamente.</p>
<p>Voltando agora os arrays até organizarmos o array da esquerda.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586102188/0b523144-1510-49ae-8b14-de4164606250.png" alt /></p>
<p>Agora vamos organizar o array da direita.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586103296/3d7d40e5-e410-4ff6-b42c-5e41372a229a.png" alt /></p>
<p>Passo 1: Escolha um elemento como pivô. Vamos escolher o 10.</p>
<p>Passo 2: Particione o array de forma que todos os elementos menores que o pivô fiquem antes dele e os maiores fiquem depois.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586105219/9aacc718-2522-4392-800a-0111b53d6cf0.png" alt /></p>
<p>Agora que chegamos no caso base, vamos ordenar os subarrays recursivamente.</p>
<p>Voltando agora os arrays até organizarmos o array da direita.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586108528/2a0e8dcd-a3ec-4a26-b4fe-1221e875c665.png" alt /></p>
<p>Agora que organizamos os subarrays, o array original está ordenado.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728586110191/caaef6a6-ec30-4b91-ab22-9d7ad14273e2.png" alt /></p>
<p>Esse é um exemplo de como o Quicksort funciona.</p>
<h3 id="heading-performance">Performance</h3>
<p>Dentro do desenvolvimento de software, a performance é um fator muito importante e muitas vezes usamos a Notação Big O para descrever a eficiência de um algoritmo.</p>
<p>Caso você não conheça a Notação Big O, recomendo que você leia um artigo sobre o assunto que escrevi aqui nesse link: <a target="_blank" href="https://medium.com/@sschonss/introdu%C3%A7%C3%A3o-a-algoritmos-nota%C3%A7%C3%A3o-big-o-d1d555b5e0e9">Notação Big O</a>.</p>
<p>O desempenho do Quicksort é O(n log n) no melhor caso e O(n²) no pior caso.</p>
<p>E a performance vai depender do pivô que você escolher. Se você escolher um pivô que seja o menor ou o maior elemento do array, o desempenho do Quicksort será O(n²). Mas, se você escolher um pivô que seja o elemento do meio do array, a performance do Quicksort será O(n log n).</p>
<p>Mas como escolher o pivô? Existem várias formas de escolher o pivô e a escolha do pivô vai depender do seu problema. Uma forma de escolher o pivô é escolher o elemento do meio do array.</p>
<p>Dessa forma, eu fiz um repositório no github com a implementação do Quicksort em Go. Caso você queira ver a implementação, clique <a target="_blank" href="https://github.com/sschonss/quicksort">aqui</a>.</p>
<p>Esse artigo é uma introdução ao Quicksort e espero que você tenha entendido como o algoritmo funciona, e agora se você escutar alguém falando sobre Quicksort, você vai entender do que se trata.</p>
<p>Lembre-se que a prática leva a perfeição e, quanto mais você praticar, mais você vai entender sobre o assunto.</p>
<p>Caso tenha alguma dúvida, busque por mais exemplos e tente resolver problemas utilizando Quicksort.</p>
<p>Espero que você tenha gostado do artigo e até a próxima!</p>
]]></content:encoded></item></channel></rss>