# Thiago Goulart de Oliveira - Full Public Content This file mirrors the public, indexable content map for AI assistants and browser agents. ## About Engineering Director with 24+ years of experience in enterprise AI platforms, solution architecture, cloud-native systems, product engineering, and engineering leadership. - I currently lead the architecture and engineering delivery of enterprise AI products, including conversational and agent-based systems that combine LLM orchestration, structured data, APIs, Text-to-SQL, and cloud-native deployment. ## Public Pages - [Home](https://tgoliveira11.tech/) - [Articles](https://tgoliveira11.tech/blog) - [Categories](https://tgoliveira11.tech/categories) - [Tags](https://tgoliveira11.tech/tags) - [Search](https://tgoliveira11.tech/search) - [About](https://tgoliveira11.tech/about) - [RSS](https://tgoliveira11.tech/rss.xml) - [Sitemap](https://tgoliveira11.tech/sitemap.xml) - [Full AI content](https://tgoliveira11.tech/llms-full.txt) ## Articles ## From Concept to Commercialization: Building and Scaling a B2B Mobility Platform URL: https://tgoliveira11.tech/blog/2026-07-24-from-concept-to-commercialization-building-and-scaling-a-b2b-mobility-platform Published: 2026-07-24 Updated: 2026-07-24 Category: Technology Strategy Tags: b2b-platform, cloud-architecture, digital-transformation, engineering-leadership, entrepreneurship, fintech, mobility, product-engineering, product-strategy, team-scaling Excerpt: A case study on turning TecleTaxi Corporativo from a product concept into a commercial B2B mobility platform—and the technical, organizational, and business lessons learned along the way. *A case study on product development, cloud architecture, fundraising, organizational growth, and the difficult work of turning an idea into an operating business.* Some products begin with a new technology. TecleTaxi Corporativo began with a gap. Companies needed a better way to manage employee transportation, service providers, authorized rides, payments, reporting, and operational control. Consumer mobility applications were becoming visible, but corporate transportation involved a different set of requirements. A company did not simply need to request a vehicle. It needed to know: - who was authorized; - which department or cost center should be charged; - whether the trip complied with policy; - how payment would be processed; - which records were required for reconciliation; - how the service would be monitored; - who would respond when something went wrong; - how transportation providers could be coordinated. This was the context in which TecleTaxi Corporativo was conceived. The goal was to create a B2B mobility platform combining transportation, payment, corporate controls, and operational visibility. Building the software was difficult. Building the company around it was harder. ## Identifying the Real Product The first version of an idea is usually expressed through features: - ride requests; - driver and vehicle location; - authentication; - corporate accounts; - payment processing; - administration; - reporting; - operational support. A feature list does not define a product. The real product was the operating model connecting employees, corporate customers, transportation providers, payments, and support. That distinction mattered. A consumer mobility application can optimize primarily for convenience and transaction volume. A corporate platform must also optimize for control, accountability, predictable billing, permissions, reporting, and contractual obligations. The buyer, user, operator, and payer may all be different people. That changes almost every design decision. ## The Problem We Were Trying to Solve Corporate transportation was often managed through phone calls, spreadsheets, paper vouchers, invoices, local suppliers, and manual reconciliation. This created: - limited usage visibility; - inconsistent authorization; - delayed reconciliation; - difficult contract management; - weak auditability; - dependency on individuals; - limited planning data; - poor incident experience. The opportunity was not simply to digitize ride requests. It was to manage mobility as an operational capability. The platform needed to serve: - corporate administrators; - operational teams; - finance; - transportation providers; - drivers; - employees; - support teams; - commercial stakeholders. Every group had different priorities. The platform needed to reconcile them. ## From Concept to a Commercial Product The early phase combined discovery, customer conversations, technical experimentation, and commercial validation. We needed to determine whether companies would pay for the service and which problems were important enough to change existing behavior. Some requirements appeared obvious but became more complex in real use. A corporate ride was not always associated with one employee. It might involve a visitor, shift, project, incident, or service provider. Authorization could depend on: - time; - role; - department; - location; - cost center; - contract; - vehicle type; - approval rules. Payments involved more than charging a card. The platform needed to connect the operational event—the completed journey—to financial records, agreements, pricing, and reconciliation. Identity, payment, mobility, and reporting were therefore not isolated features. They were parts of the same business transaction. ## Building the Architecture TecleTaxi was designed as a cloud-based platform on AWS. It needed to support: - web and mobile experiences; - corporate administration; - driver and provider interactions; - geolocation; - authentication; - payments; - external integrations; - operational monitoring; - reporting; - customer support. At the time, microservices were increasingly popular. Adopting a highly distributed architecture too early would have introduced operational and organizational costs the company was not prepared to absorb. We chose a modular approach. Capabilities were separated around areas such as: - identity; - corporate accounts; - mobility operations; - payments; - geolocation; - notifications; - reporting; - administration. This preserved clear boundaries without requiring a large number of independently deployed services from the beginning. The decision reflected a principle: > Architecture should match the organization's current capability while preserving reasonable options for future growth. A fashionable architecture would not help if the team could not operate it reliably. ## Geolocation and Operational State Location mattered, but geolocation alone was not the product. The platform needed to combine location events with operational state: - Was the ride accepted? - Was the driver approaching? - Had the passenger boarded? - Was the journey in progress? - Had it completed? - Had an exception occurred? - Was the transaction ready for financial processing? Location data could be delayed, incomplete, or inaccurate. Connectivity varied. Drivers and passengers could act outside the expected sequence. The system needed to tolerate imperfect real-world behavior rather than assume every event would arrive correctly and in order. The physical world does not behave like a controlled software test. ## Payments and Authentication Payments and authentication were among the most sensitive capabilities. Corporate customers needed confidence that: - only authorized users could request services; - transactions were associated with the correct account; - records could be reconciled; - sensitive data was protected; - changes were traceable; - access could be revoked; - administrative actions were controlled. Legacy payment and authentication components had to evolve as the platform matured. Replacing a critical system creates risk. Keeping it indefinitely creates another kind of risk. The practical approach was incremental: 1. Identify the highest-risk limitations. 2. Establish clearer boundaries. 3. Introduce modern interfaces. 4. Migrate capabilities gradually. 5. Maintain traceability. 6. Avoid changing too many critical components at once. The objective was not modernization for its own sake. It was to improve scalability, security, and control without destabilizing the business. ## Building More Than an Engineering Team As the product progressed, the organization expanded to approximately 50 professionals across: - engineering; - product; - design; - marketing; - commercial operations; - customer support; - administration. Scaling changed the nature of the work. In a small team, information moves through informal conversations. As the organization grows, that stops working. We needed: - clearer responsibilities; - explicit priorities; - structured feedback; - one-on-ones; - development plans; - cross-functional coordination; - predictable decision-making; - stronger communication between business and technology. Adding people does not automatically increase delivery capacity. Without clarity, coordination cost can grow faster than output. Leadership became less about personally solving each problem and more about creating conditions in which others could solve them. ## Cross-Functional Leadership The most difficult decisions rarely belonged to a single department. A product change could affect: - engineering complexity; - customer contracts; - operational processes; - support workload; - marketing commitments; - financial reconciliation; - provider relationships. Technical teams needed business context. Commercial teams needed delivery context. Operations needed visibility into product changes. Product decisions needed to account for customer demand and platform sustainability. Leadership was not about eliminating disagreement. It was about ensuring disagreement happened with shared information and explicit trade-offs. ## Fundraising and Expansion In 2015, I led a funding round that supported expansion. Fundraising is often described as a milestone. It is better understood as a commitment. New capital creates options, but also expectations around growth, execution, governance, and results. The organization needed to translate investment into: - product development; - team growth; - commercial expansion; - operational capability; - customer acquisition; - reliability. Capital does not solve a weak operating model. It amplifies the model that already exists. If priorities are unclear, funding accelerates waste. If assumptions are weak, it scales the wrong solution. If leadership systems are immature, additional people increase confusion. The funding round increased the importance of disciplined execution. ## Commercialization Turning a product into a commercial offering required more than releasing software. Corporate customers needed confidence in the complete service: - demonstrations; - contract negotiation; - onboarding; - account configuration; - provider coordination; - support; - billing and reconciliation; - incident handling; - reporting; - relationship management. The product became commercially real when companies incorporated it into daily operations. By 2018, TecleTaxi supported multiple active corporate contracts. That mattered more than whether every original feature had been implemented exactly as imagined. Commercialization proved that the platform addressed a real operational need. It also exposed the difference between adoption and operation. Winning a contract creates the opportunity. Delivering consistently creates the relationship. ## Measuring What Mattered We monitored indicators such as: - availability; - incident-resolution time; - customer satisfaction; - contract growth; - operational responsiveness. Definitions mattered as much as values. Availability could not mean infrastructure uptime alone. A platform might be technically online while payment, provider workflow, or authentication was unavailable. Incident resolution could not mean merely closing a support ticket. Useful metrics represent customer and business outcomes, not internal activity. ## What Was Harder Than Expected ### Enterprise Customers Need Predictability Innovation attracts interest. Reliability, support, governance, and trust retain customers. ### Operational Exceptions Become the Product The expected workflow is simple. Complexity appears when a driver cancels, connectivity fails, payment is rejected, a journey is disputed, or a customer needs an exception. The platform had to support the people resolving those situations. ### Growth Exposes Organizational Weakness A small team can compensate for unclear process through effort and proximity. A larger organization cannot. ### Architecture and Business Cannot Be Separated A technical decision affects cost, delivery, workload, and commercial commitments. A commercial decision can introduce architectural complexity or security risk. ### The Leader's Role Must Change Being involved in every decision can initially provide speed and control. Eventually, it creates dependency. Leadership must move toward context, delegation, standards, feedback, and accountability. ## Decisions I Would Preserve ### Building Around a Real Business Constraint The platform addressed corporate mobility rather than reproducing a consumer application for a different audience. ### Choosing a Modular Architecture Modularity preserved capability boundaries without introducing distributed complexity too early. ### Treating Payments and Identity as Core Capabilities They were central to trust and operational control. ### Building Cross-Functional Capability Engineering alone could not create a commercial platform. ### Measuring Operational Outcomes Availability, response, satisfaction, and contract development offered a more realistic view than feature delivery alone. ## What I Would Approach Differently Today ### Explicit Product and Domain Boundaries Clearer ownership would reduce ambiguity as the platform and team expanded. ### Stronger Observability Technical metrics, business events, and customer-impact signals should be connected from the start. ### Earlier Architecture Decision Records Important trade-offs should be documented while context is fresh. ### More Structured Experimentation Product assumptions should be expressed as testable hypotheses. ### Earlier Management Systems Hiring and growth should be accompanied by explicit operating principles, roles, feedback cycles, and decision rights. These are not criticisms made with hindsight. They are evidence that building a company changes how architecture, product, and leadership are evaluated. ## Lessons From the Journey 1. **A product is also an operating model.** Support, contracts, onboarding, payments, reconciliation, providers, and incidents are part of the experience. 2. **Architecture must match organizational maturity.** The best architecture is one the organization can build, understand, operate, and evolve. 3. **Enterprise trust is built through unremarkable capabilities.** Authorization, reporting, reconciliation, auditability, support, and predictable operations enable adoption. 4. **Growth requires leadership systems.** A larger team needs clear ownership, expectations, feedback, communication, and decisions. 5. **Funding increases accountability.** Investment increases the cost of unclear priorities and weak execution. 6. **Commercialization is different from development.** The organization must sell, onboard, support, operate, bill, and retain. 7. **End-to-end accountability changes technical judgment.** Architecture cannot be evaluated in isolation from people, customers, operations, and capital. ## How the Experience Shaped My Later Work After the Managing Director role, I returned to software and solution architecture. That transition may have looked unusual. In practice, TecleTaxi made me a better architect. I had learned to evaluate decisions through a broader lens: - customer value; - organizational capability; - operational risk; - delivery constraints; - financial sustainability; - team ownership; - long-term change. Years later, while working with cloud platforms, data systems, and enterprise AI products, the underlying questions remained familiar. Can the organization operate what we are designing? Can users trust the result? What happens when a dependency fails? Who owns the outcome? Does the complexity create enough value to justify its cost? Technology changes. The need for accountable engineering does not. ## Conclusion TecleTaxi Corporativo began as an attempt to solve a practical B2B mobility problem. It became a lesson in product development, cloud architecture, payments, operations, fundraising, commercialization, team scaling, and leadership. The most important outcome was not a feature or technology. It was understanding that successful products emerge from the alignment of several systems: - software; - business model; - organization; - operational process; - customer relationship; - decisions made under pressure. Building the platform required technical knowledge. Commercializing and scaling it required something broader: connecting technology, people, capital, operations, and customer needs around a shared outcome. That remains one of the most valuable lessons of my career. --- ## What Breaks First When Text-to-SQL Moves from Demo to Production? URL: https://tgoliveira11.tech/blog/2026-07-24-what-breaks-first-when-text-to-sql-moves-from-demo-to-production Published: 2026-07-24 Updated: 2026-07-24 Category: AI Engineering Tags: agentic-systems, ai-observability, data-engineering, data-governance, enterprise-ai, generative-ai, llmops, responsible-ai, solution-architecture, text-to-sql Excerpt: A practical look at semantic ambiguity, authorization, validation, observability, and the operational risks hidden behind a convincing conversational interface. *A practical look at semantic ambiguity, authorization, validation, observability, and the operational risks hidden behind a convincing conversational interface.* The first Text-to-SQL demonstration usually feels almost magical. An operator opens a GPT-like interface and asks: > How much product is available for tomorrow's operation? Within seconds, the system generates SQL, retrieves data, and presents a clear answer. There is no dashboard to navigate, no manual join, and no wait for an analyst. The demonstration succeeds because the question is controlled, the schema is known, the data is clean, and everyone already understands the expected result. Production is different. In a real operational environment, “available product” might mean physical inventory, uncommitted inventory, commercially available inventory, or inventory adjusted for temperature, density, quality, location, and contractual restrictions. “Tomorrow” might refer to local time, a corporate time zone, or an operating day that begins at 6:00 a.m. rather than midnight. A result can be technically valid and operationally wrong. That distinction is where many Text-to-SQL implementations begin to break. ## A Hypothetical Production Scenario Consider a fictional product called **Ops Intelligence**, designed for operators working in a complex industrial environment. It provides a conversational interface over approved enterprise data. Operators use it throughout the day to retrieve: - current inventory; - scheduled movements; - equipment availability; - production volumes; - quality measurements; - operational restrictions; - contractual limits; - historical performance; - active incidents. The experience resembles a general-purpose AI assistant, but with one fundamental difference: **It does not search the open internet.** Its answers must come exclusively from controlled, traceable, and reviewed enterprise sources. That constraint is intentional. An operator cannot make a high-value decision based on an unverified website, an outdated document, or content whose origin is unclear. The system might be asked: > What is the adjusted available volume at Terminal North for the next operating window? A wrong answer could result from something as small as: - using gallons instead of barrels; - treating kilograms as metric tons; - applying the wrong density conversion; - using gross volume instead of net standard volume; - interpreting a decimal comma as a thousands separator; - converting a timestamp into the wrong time zone; - rounding too early; - reading an outdated quality measurement; - including inventory that has already been allocated. Each mistake may look small in isolation. At operational scale, it can translate into contractual penalties, interrupted operations, safety exposure, or losses measured in millions. In this environment, Text-to-SQL is not simply a productivity feature. It becomes part of a decision-support system. That changes the engineering problem completely. ## 1. Semantic Ambiguity Appears Before SQL Generation The first production problem is not usually SQL syntax. It is language. Operators use expressions whose meaning depends on role, location, operation, and business context. Consider: > What is today's available volume? Before generating a query, the system must resolve: - Which facility? - Which product? - Which storage location? - Gross or net volume? - Measured or calculated volume? - Physical stock or commercially available stock? - Calendar day or operational day? - Should committed quantities be excluded? - Which unit should be used? - Which quality-adjustment rules apply? A language model can generate flawless SQL against the wrong interpretation. This is dangerous because the result often looks plausible. The system should not hide ambiguity behind a confident answer. It should make ambiguity visible. A safer response might be: > I found three definitions of available volume. Do you mean physical inventory, uncommitted inventory, or quality-adjusted operational inventory? That may feel less impressive than an immediate answer, but it is substantially safer. In production, clarification is not friction. It is a control. ### A Semantic Layer Helps A reliable platform needs business concepts that are independent of raw table and column names. Instead of teaching the model that `qty_avl_adj` matters, define what **adjusted available quantity** means: - source systems; - calculation rules; - units; - exclusions; - freshness requirements; - ownership; - authorized audiences; - known limitations. Text-to-SQL becomes more reliable when it operates over governed business concepts rather than reverse-engineering meaning from database metadata. ## 2. Authorization Cannot Be Added After Query Generation A conversational interface changes how users access data, but it must not change what they are permitted to see. Suppose an operator asks: > Show me all delayed shipments and their financial exposure. The generated SQL may be valid, but the answer could combine operational data with commercially sensitive contract values the operator is not authorized to access. Traditional applications often enforce authorization through predefined screens and APIs. Text-to-SQL removes many fixed paths. Users can express combinations of filters, joins, and aggregations that were never explicitly designed. This increases the authorization surface. Controls may be required at several levels: - source; - database; - schema; - table; - column; - row; - business concept; - output field; - conversational context. A user may view shipment status but not contract value. They may access one facility but not another. They may see records but not cross-business-unit aggregates. The assistant also needs protection against indirect disclosure. A user without access to individual values might infer them through totals, differences, or narrow filters. ### Policy Must Remain Outside the Model Authorization should be resolved before execution and enforced independently of the language model. A safer design combines: - enterprise identity; - role-based and attribute-based access; - row- and column-level security; - approved semantic entities; - policy enforcement outside the model; - output filtering; - auditable access decisions. The model can help construct a query. It should never decide whether the user is entitled to the result. ## 3. Schema Drift Can Produce Answers That Still Look Correct Production systems evolve. Columns are renamed. Tables are replaced. Pipelines move. New sources are introduced. A calculated field changes meaning. A unit previously stored in kilograms begins arriving in grams. A timestamp becomes UTC instead of local time. A query created last month may still execute after one of those changes. That can be worse than a query that fails. A failed query creates an incident. A successful query against changed semantics creates a credible wrong answer. Schema drift therefore includes more than structure: - column and table changes; - type changes; - source ownership changes; - unit changes; - business-definition changes; - calculation changes; - freshness changes; - quality-rule changes. A production platform needs versioned metadata and explicit contracts between data producers and the conversational layer. When a critical definition changes, the platform should know: - what changed; - when it changed; - which semantic entities are affected; - whether previous evaluations remain valid; - whether related questions must be temporarily blocked. In a high-risk environment, “temporarily unavailable due to a definition change” is better than a confident answer based on an obsolete assumption. ## 4. Query Validation Must Examine Intent, Not Only Syntax A generated query can parse correctly and still be unsafe, expensive, or semantically wrong. Before execution, validation should inspect: - sources; - selected fields; - joins; - filter scope; - aggregation; - date ranges; - unit transformations; - destructive operations; - expected result size; - estimated execution cost; - authorization policy; - business rules. Read-only credentials are necessary, but insufficient. A read-only query can still: - scan billions of rows; - overload a warehouse; - expose restricted data; - create misleading aggregates; - apply an incorrect conversion; - return stale records; - mix incompatible units; - join data at the wrong grain. For adjusted operational volume, validation might require that the query: 1. use the approved inventory view; 2. filter by an authorized facility; 3. select the latest certified measurement; 4. apply the approved density and temperature adjustment; 5. exclude allocated inventory; 6. return the configured operational unit; 7. preserve required precision; 8. include the data timestamp. Critical rules should be deterministic. They should not depend solely on probabilistic model judgment. ## 5. Evaluation Is More Than Comparing SQL Strings A common development metric is: > Did the model generate the expected SQL? That is useful, but insufficient. Different SQL statements can produce the same correct result. A query that matches a reference statement can also become wrong when data or semantics change. Evaluation should test the complete answer path. ### Intent Interpretation Did the system understand the operator's meaning? ### Authoritative Source Selection Did it use the approved source? ### Query Correctness Are joins, filters, dates, aggregations, and transformations correct? ### Authorization Did the workflow respect access boundaries? ### Unit and Formatting Accuracy Were units, conversions, precision, dates, and locale-specific formats correct? ### Grounding Can every material statement be traced to returned data? ### Operational Usefulness Does the response support the actual decision? ### Uncertainty Behavior Did the system ask for clarification when necessary? ### Failure Behavior Did it refuse when data was stale, unavailable, unauthorized, or unreliable? A production evaluation set should include realistic and adversarial cases: - ambiguous language; - conflicting terminology; - incomplete filters; - stale data; - schema changes; - unauthorized requests; - incompatible units; - empty results; - outliers; - malformed values; - complex follow-up questions. The evaluation set must evolve with production usage. Real operator questions expose ambiguities that design teams rarely anticipate. ## 6. Hallucination Can Happen After the Database Returns the Correct Result Text-to-SQL discussions often focus on invented tables or columns. That is only one failure category. The model may generate a correct query, receive correct rows, and misrepresent them in natural language. For example: | Product | Available quantity | Unit | |---|---:|---| | Product A | 18,450.75 | m³ | The assistant says: > Product A has approximately 18.5 million cubic meters available. The query was correct. The explanation was not. Other failures include: - changing units during summarization; - recalculating incorrectly; - confusing percentages and absolute values; - reversing an increase and decrease; - inventing a cause; - omitting a qualifier; - describing stale data as current; - merging values from different locations; - presenting an estimate as a measurement. For high-value numerical answers, free-form transformation should be minimized. Important values should pass through deterministic formatting and conversion functions. A robust response also carries provenance: > **Adjusted available volume:** 18,450.75 m³ > **Facility:** Terminal North > **Data certified at:** 14:25 local time > **Source:** Operational Inventory View v3.2 > **Excludes:** Allocated and quality-restricted inventory Traceability makes it harder for polished language to hide a wrong assumption. ## 7. Observability Must Capture Decisions, Not Only Errors Traditional monitoring focuses on latency, exceptions, CPU, memory, and availability. A Text-to-SQL platform also needs visibility into how an answer was produced. For each interaction, the system should be able to reconstruct: - original question; - interpreted intent; - clarification; - identity and authorization context; - semantic entities; - schema version; - generated SQL; - validation results; - execution details; - sources; - row counts; - freshness; - transformations; - final answer; - risk classification; - user feedback; - escalation. Logs may themselves contain sensitive data and require governance. The objective is accountable traceability, not unrestricted logging. When an operator reports an incorrect answer, the team must distinguish among: - misunderstood intent; - incorrect metadata; - invalid join; - stale data; - conversion error; - authorization issue; - summarization error; - source-system defect. Useful metrics include: - clarification rate; - validation rejection rate; - unauthorized-request rate; - execution success; - answer correction; - stale-data detection; - operator acceptance; - escalation; - query cost; - end-to-end latency; - recurring ambiguity; - performance by business domain and complexity. The useful question is not how often the system answers. It is how often it produces an answer a qualified operator can safely use. ## 8. Latency Changes Operator Behavior A ten-second demo can impress an audience. The same delay repeated throughout a shift can destroy adoption. Latency accumulates across: 1. intent classification; 2. metadata retrieval; 3. semantic resolution; 4. authorization; 5. SQL generation; 6. validation; 7. execution; 8. result processing; 9. response generation. A slow workflow may cause operators to abandon the tool, repeat questions, return to legacy dashboards, or make decisions using older information. Latency cannot be reduced by removing safeguards. The objective is not the fastest answer. It is the fastest safe answer. Useful strategies include: - cache approved metadata rather than uncontrolled answers; - precompute common operational metrics; - use governed analytical views; - route simple questions through deterministic templates; - classify complexity before expensive workflows; - separate data retrieval and narrative generation; - enforce query timeouts and scan limits; - optimize data products for conversational access. The interface should communicate controlled work: > Checking the latest certified inventory and applying the standard-volume adjustment. A short explanation is better than an unexplained spinner. ## 9. Cost Grows Through Small, Repeated Decisions The cost of a Text-to-SQL platform includes more than model tokens: - metadata retrieval; - vector search; - model calls; - warehouse compute; - retries; - validation; - observability; - evaluation; - human review; - incident investigation. A poorly constrained question can scan years of high-volume data. A model can retry repeatedly after failures. Controls should exist throughout the workflow: - classify question complexity; - use smaller models when appropriate; - limit context to relevant metadata; - enforce query budgets; - estimate warehouse cost; - reject unbounded queries; - cache safe intermediate results; - route common questions to governed metrics; - monitor cost per successful answer; - monitor cost by use case and domain. Cost must also be assessed against business value. A more expensive answer can be justified for a high-value decision. The same cost may be unacceptable for a routine question that a governed metric can answer. ## 10. Human Escalation Is Part of the Product In operational systems, escalation is a safety mechanism. The assistant should stop when: - multiple valid interpretations remain; - the authoritative source is unavailable; - freshness exceeds the permitted threshold; - required fields are missing; - systems conflict; - a conversion cannot be verified; - access is insufficient; - the result is outside normal ranges; - the decision carries substantial financial or safety impact; - confidence is below the required threshold. A useful escalation provides structure: > I cannot confirm the adjusted available volume because the latest quality measurement has not been certified. The physical inventory is available, but producing an adjusted value would require an unverified density reading. > > **Suggested next step:** Contact Quality Operations or retry after certification. The assistant should never compensate for missing evidence by inventing certainty. ## The Production Architecture Is More Than an LLM Connected to a Database A dependable platform requires controlled layers: 1. **Approved data products** — curated views and governed analytical models. 2. **Semantic definitions** — explicit business concepts, units, calculations, ownership, and freshness. 3. **Identity and policy enforcement** — external to the model. 4. **Controlled query generation** — only relevant and permitted metadata. 5. **Deterministic validation** — critical rules and conversions. 6. **Safe execution** — read-only connections, timeouts, limits, and cost controls. 7. **Grounded response generation** — preserve units, precision, timestamps, and qualifiers. 8. **Traceability** — sources and data timestamps visible. 9. **Continuous evaluation** — real questions, changed schemas, and failure cases. 10. **Human intervention** — clear paths for clarification, review, and escalation. None of these layers is optional in a high-risk operational environment. ## What Usually Breaks First? The visible failure may be a wrong query, an incorrect unit, or a slow response. The underlying failure usually happens earlier: the system lacks a controlled definition of truth. It does not clearly know: - what a business term means; - which source is authoritative; - who can access it; - how current it must be; - which transformations are permitted; - when uncertainty is acceptable; - when a human must take over. A demo proves that a model can translate language into a query. A production platform must prove something harder: > The complete path from human intent to operational answer is controlled, explainable, authorized, observable, and safe. A production Text-to-SQL platform is not a chatbot with database access. It is a governed decision-support system with a conversational interface. The interface may look simple. The engineering behind a trustworthy answer cannot be. --- ## Building an Opinionated Authentication Foundation for Next.js and PostgreSQL URL: https://tgoliveira11.tech/blog/2026-06-16-opinionated-authentication-foundation Published: 2026-06-16 Updated: 2026-07-24 Category: Software & Solution Architecture Tags: account-security, app-router, audit-logs, auth-package, authentication, developer-tools, drizzle-orm, email-verification, full-stack-development, next-auth, nextjs, npm-package, open-source, passkeys, password-reset, postgresql, rate-limiting, saas-development, secure-auth, session-management, totp, two-factor-authentication, typescript, web-security, webauthn Excerpt: A technical overview of `@tgoliveira/secure-auth`: an opinionated authentication foundation for Next.js, PostgreSQL, and Drizzle, designed to reduce repeated auth work across projects while keeping clear boundaries, security controls, and honest experimental limitations. **GitHub:** [github.com/tgoliveira11/next-secure-auth-starter](https://github.com/tgoliveira11/next-secure-auth-starter) **Package:** [`@tgoliveira/secure-auth`](https://www.npmjs.com/package/@tgoliveira/secure-auth) Authentication is one of those parts of a product that looks simple until it is not. At first, it is “just login”. Then comes registration, password reset, email verification, OAuth, account linking, sessions, revocation, password policy, rate limiting, audit logs, two-factor authentication, passkeys, account deletion, and the inevitable question: *are we doing this safely enough?* For many Next.js applications, especially internal tools, SaaS products, and early-stage platforms, this work gets rebuilt again and again. Sometimes it starts as a starter app. Sometimes it grows organically inside the product. Sometimes it becomes a collection of copied files passed from one repository to another. `@tgoliveira/secure-auth` is an attempt to solve that problem in a more deliberate way. It is an opinionated, experimental authentication package for **Next.js App Router**, **TypeScript**, **PostgreSQL**, and **Drizzle ORM**. Its goal is not to be a universal authentication platform. Its goal is narrower: provide a reusable account-authentication foundation for teams that are already building in this stack and do not want to rewrite the same security-sensitive flows in every application. --- ## The problem: authentication is rarely just authentication A modern product rarely needs only a username and password form. Even a relatively small application may need: | Area | Common implementation work | | ----------------- | ------------------------------------------------------------------------------- | | Credentials | Register, login, password hashing, password reset, password change | | OAuth | Provider configuration, callbacks, account linking, error handling | | Email flows | Verification links, reset links, resend throttling | | Sessions | Server-side session records, revocation, device metadata | | Security controls | Rate limits, audit logs, token hashing, safe logging | | 2FA | TOTP setup, recovery codes, verification during login | | Passkeys | WebAuthn challenges, credential storage, browser-side flows | | UI | Login, register, forgot password, account settings, sessions, security settings | | Database | Auth schema, migrations, indexes, repositories | None of these things are impossible to build. The issue is that they are easy to build inconsistently. One product gets a better password policy. Another forgets token hashing. One app has session revocation. Another only supports logout from the current browser. One team fixes an account enumeration risk, but the fix never reaches the other codebases. That is the real cost: not just the first implementation, but the maintenance drift across products. --- ## What `@tgoliveira/secure-auth` is `@tgoliveira/secure-auth` is a package-first authentication layer built around a single composition root: ```ts createSecureAuth(config) ``` The consumer application provides infrastructure: * the PostgreSQL database connection; * the Drizzle client; * secrets and environment mapping; * OAuth configuration; * WebAuthn configuration; * the email transport; * route and page mounting inside the Next.js app. The package provides the authentication domain: * schema and migrations; * services and repositories; * API route handlers; * password policy; * email verification; * password reset; * OAuth integration through NextAuth v4; * passkeys through WebAuthn; * TOTP 2FA and backup codes; * session management; * audit events; * rate limiting; * optional ready-made UI pages and components. In practical terms, the application owns deployment-specific concerns, while the package owns the reusable authentication behavior. That boundary is the core idea. --- ## Why not just keep a starter app? A starter app is useful at the beginning. It proves that the flows work. It gives you something concrete to copy from. It can show how the architecture is supposed to look. But a starter app has a major limitation: once copied, it stops being a shared system. Every consumer becomes its own fork. Security fixes do not automatically propagate. A password-reset improvement has to be manually copied. A 2FA fix may land in one app but not another. Documentation starts drifting from implementation. That is why `secure-auth` moved toward a package-first model. The repository can still contain a starter app, but the starter is no longer the product. The package is the product. The starter and demo apps exist to validate that a real consumer can integrate the package using public exports only. That distinction matters. A starter helps you begin once. A package helps you maintain many times. --- ## The architecture in one picture Conceptually, the integration looks like this: ```txt ┌──────────────────────────────┐ ┌──────────────────────────────┐ │ Consumer Next.js app │ │ @tgoliveira/secure-auth │ ├──────────────────────────────┤ ├──────────────────────────────┤ │ PostgreSQL + Drizzle client │ ─db─▶ │ Services and repositories │ │ Environment mapping │ ─cfg▶ │ createSecureAuth(config) │ │ EmailProvider implementation │ ─mail▶│ Email orchestration │ │ Thin API route wrappers │ ─api▶ │ secureAuth.routes.* │ │ Thin page wrappers │ ─ui─▶ │ LoginPage, RegisterPage, etc. │ │ SecureAuthUIProvider │ ◀cfg─ │ secureAuth.uiConfig │ └──────────────────────────────┘ └──────────────────────────────┘ ``` The package intentionally does **not** read `process.env` directly. That responsibility stays in the application. This makes the package easier to test, easier to reason about, and less coupled to a specific deployment environment. A typical bootstrap file looks like this: ```ts import "server-only"; import { createSecureAuth } from "@tgoliveira/secure-auth/next"; import { db } from "@/lib/db"; import { emailProvider } from "@/lib/email-provider"; export const secureAuth = createSecureAuth({ db, app: { name: "My App", slug: "my-app", baseUrl: process.env.APP_BASE_URL!, }, auth: { secret: process.env.NEXTAUTH_SECRET!, twoFactorEncryptionKey: process.env.TWO_FACTOR_SECRET_ENCRYPTION_KEY!, afterLoginPath: "/dashboard", }, email: { from: "My App ", provider: emailProvider, }, }); ``` Then each route can be mounted with a thin wrapper: ```ts // app/api/auth/register/route.ts import { secureAuth } from "@/lib/secure-auth"; export const POST = secureAuth.routes.register.POST; ``` And UI pages can be mounted just as thinly: ```tsx // app/login/page.tsx export { LoginPage as default } from "@tgoliveira/secure-auth/react"; ``` Or customized where needed: ```tsx import { LoginPage } from "@tgoliveira/secure-auth/react"; export default function Page() { return ; } ``` This is the intended integration style: the app wires, the package implements. --- ## What the package gives you The package is not only an OAuth wrapper. It is closer to an account-authentication layer for a specific product stack. It includes support for: * email/password registration and login; * password hashing; * forgot/reset password flows; * change password; * email verification; * OAuth through NextAuth v4; * Google, Apple, and Microsoft-style provider configuration; * passkey registration and login through WebAuthn; * TOTP-based two-factor authentication; * encrypted 2FA secrets; * backup codes; * account sessions; * session revocation; * optional single active session policy; * rate limiting; * audit events; * safe logging patterns; * token hashing at rest; * account deletion with re-authentication safeguards; * Drizzle schema exports; * package migrations; * ready-made React pages and components; * configurable UI defaults through `SecureAuthUIProvider`. This is useful because these features usually need to work together. For example, password policy is not only a form validation concern. It affects registration, reset password, change password, UI feedback, and server-side enforcement. Session revocation is not only a database operation. It affects account settings, logout behavior, polling, and client state. Packaging these concerns together makes the system more coherent. --- ## What the package does not do The package is deliberately scoped to account authentication. It does **not** provide: * product-specific authorization rules; * billing; * tenant management; * marketing pages; * encrypted domain data; * vault-style encryption; * application-specific user profiles; * a built-in SMTP or SendGrid transport; * a framework-agnostic API; * support for every database or ORM; * a production-ready security guarantee. That last point is important. `@tgoliveira/secure-auth` is published and functional, but it is still labeled as an experimental internal release in the `0.1.x` line. That means it is worth evaluating, testing, learning from, and possibly using in controlled contexts, but it should not be treated as a stable 1.0 production contract without review. Authentication packages deserve that honesty. --- ## Why use it instead of only NextAuth? NextAuth is a strong foundation for OAuth providers, callbacks, session handling, and the familiar Next.js authentication route pattern. Many applications use it successfully. But NextAuth by itself does not give you a complete account center. You still have to decide how to implement: * your user schema; * password registration; * email verification; * reset password tokens; * password policy; * passkeys; * 2FA; * backup codes; * session revocation UI; * account deletion; * audit logging; * rate limiting; * application-specific account pages. That is where `@tgoliveira/secure-auth` positions itself. It does not replace NextAuth so much as wrap a broader account-authentication system around it for a specific stack. A fair summary would be: > NextAuth gives you authentication primitives. `@tgoliveira/secure-auth` tries to give you a reusable account-authentication product layer for Next.js, PostgreSQL, and Drizzle. If your app only needs “Sign in with Google”, this package may be too much. If you repeatedly build products that need credentials, OAuth, passkeys, 2FA, sessions, verification, account settings, and security policies, the package starts to make more sense. --- ## When it makes sense to use This package is most interesting when several of these are true: * you are building with Next.js App Router; * you use PostgreSQL; * you use Drizzle ORM; * you want server-side session records; * you need credentials and OAuth; * you want passkeys or plan to support them; * you need 2FA; * you care about email verification and password reset flows; * you are building more than one product; * you prefer an opinionated baseline over custom auth per repository; * you are comfortable reviewing an experimental package before production use. For a solo prototype, it may save time. For a team building multiple applications, it may save more than time: it may reduce security drift. --- ## When it probably does not make sense It may not be the right fit if: * you are not using Next.js; * you are not using PostgreSQL; * you use Prisma or another ORM and do not want Drizzle; * you need Auth.js v5 today; * you need only basic social login; * your product requires a deeply custom auth UX from day one; * you need a fully stable production contract immediately; * your team is not willing to review the security model. Opinionated packages are valuable when your opinions match theirs. They become friction when they do not. That is not a flaw. It is the trade-off. --- ## The customization model The package tries to avoid two extremes. It is not a black box where every behavior is hidden. It is also not a low-level toolkit where every product must assemble the whole system manually. Customization happens through a few layers: ```ts createSecureAuth({ passwordPolicy: { minLength: 12, enforcement: "warn", requireSymbol: true, blockCommonPasswords: true, }, sessions: { singleActiveSession: true, revocationPollIntervalSeconds: 10, }, }); ``` For UI, the package exposes provider-level defaults: ```tsx import { SecureAuthUIProvider } from "@tgoliveira/secure-auth/react"; import { secureAuth } from "@/lib/secure-auth"; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` And individual page props can override provider defaults: ```tsx ``` The practical precedence is: ```txt Page props → SecureAuthUIProvider config → package defaults ``` Email is also intentionally externalized. The package defines an `EmailProvider` interface, but the application decides whether it sends through SMTP, a cloud provider, a queue, or a console provider in development. ```ts import type { EmailProvider } from "@tgoliveira/secure-auth/email"; export const consoleEmailProvider: EmailProvider = { async send({ to, subject, html, text }) { console.info("[email]", { to, subject, text: text ?? html }); }, }; ``` That is a good boundary: the package owns *when* auth emails are sent and what they are for; the application owns *how* they are delivered. --- ## Security posture: useful controls, not magic The package includes several security-oriented controls: * bcrypt password hashing; * configurable password policy; * TOTP 2FA; * encrypted 2FA secrets; * backup codes; * passkeys through WebAuthn; * server-side sessions; * session revocation; * optional single active session; * hashed IP and user-agent metadata; * rate limiting; * hashed tokens at rest; * single-use time-limited tokens; * audit events; * safe logging and redaction patterns; * account deletion safeguards. These are meaningful building blocks. But security is not something a package can simply declare into existence. Correct configuration still matters. Secret management still matters. WebAuthn origins and RP IDs must be correct. Email delivery must be trustworthy. OAuth providers need to be configured carefully. The database must be migrated and operated properly. A real production deployment should still go through security review. That is especially true because the package is still in the `0.1.x-internal` phase. The right way to describe it is not “production-ready authentication solved”. The right way is: > an early, functional, opinionated authentication foundation that centralizes many of the hard parts, while still requiring careful review before production use. That is a stronger and more credible position. --- ## The interesting engineering idea: public API discipline One of the most important parts of this package is not a specific feature. It is the boundary discipline. The package exposes supported entry points such as: ```ts @tgoliveira/secure-auth @tgoliveira/secure-auth/next @tgoliveira/secure-auth/react @tgoliveira/secure-auth/react/client @tgoliveira/secure-auth/client @tgoliveira/secure-auth/drizzle/schema @tgoliveira/secure-auth/email @tgoliveira/secure-auth/styles.css ``` Consumers should not deep-import internal source files. They should not depend on internal services directly. They should create the auth system once through `createSecureAuth(config)` and consume the routes, UI config, schema, and public exports from there. This matters because packages fail when consumers accidentally couple themselves to internals. Once that happens, every refactor becomes a breaking change in practice, even if it was never meant to be public API. The presence of a minimal consumer demo is also important. It proves that a new app can integrate through public exports only. That is one of the best ways to prevent a reusable package from secretly depending on its original starter app. --- ## Trade-offs No architecture choice is free. Here are the honest trade-offs of this approach: | Trade-off | Impact | | ------------------------ | ---------------------------------------------------------------------------- | | Opinionated stack | Fast path for Next.js + PostgreSQL + Drizzle, but not useful for every team | | Package abstraction | Less copied code, but more package API to learn | | Thin wrappers | Integration is simple but still mechanical across routes and pages | | NextAuth v4 dependency | Works with a stable ecosystem, but Auth.js v5 migration would be future work | | Experimental versioning | Useful now, but breaking changes are still possible | | App-owned env mapping | Explicit and testable, but more boilerplate than magic config | | App-owned email provider | Flexible, but not plug-and-play SMTP | | Ready-made UI | Great for speed, but highly custom products may override heavily | | Security controls | Better baseline, but not a substitute for review | For me, the most important trade-off is the first one: this package becomes valuable precisely because it is not trying to support every possible stack. Generic auth abstractions often become too broad, too configurable, and too hard to reason about. `@tgoliveira/secure-auth` chooses a narrower road: solve one common stack well enough to reuse. That is a reasonable bet. --- ## So, does it make sense? Yes, with the right expectations. It makes sense if the objective is to stop rebuilding the same account-authentication foundation across multiple Next.js applications. It makes sense if the team values consistency, explicit dependency injection, PostgreSQL-backed auth data, reusable UI, and centralized security improvements. It makes less sense if the application only needs a tiny login flow, uses a different stack, or needs a fully stable production-grade contract today. The package is best understood as a reusable foundation, not a final security certification. It gives developers a strong starting point: routes, schema, services, UI, policies, and integration patterns. It also forces a healthy boundary: the app owns infrastructure and configuration; the package owns the authentication domain. That separation is the real value. --- ## Final thoughts Authentication is one of the worst places to accumulate copy-paste architecture. A starter app can help once. A package can help repeatedly. But only if the package has clear boundaries, public API discipline, a validation consumer, and an honest maturity model. `@tgoliveira/secure-auth` is not trying to be everything for everyone. It is an opinionated authentication foundation for teams building with Next.js, PostgreSQL, Drizzle, and TypeScript. For that audience, it is absolutely worth understanding. Even if you do not adopt it directly, the architectural lesson is valuable: keep infrastructure in the app, keep reusable domain logic in the package, avoid hidden environment coupling, and validate the public API with a real consumer. That is a good pattern not only for authentication, but for any internal platform package. **GitHub:** [github.com/tgoliveira11/next-secure-auth-starter](https://github.com/tgoliveira11/next-secure-auth-starter) **Package:** [`@tgoliveira/secure-auth`](https://www.npmjs.com/package/@tgoliveira/secure-auth) ![Building an Opinionated Authentication Foundation for Next.js and PostgreSQL](https://nqurbfrzrwacx2xs.public.blob.vercel-storage.com/posts/82751a10-5cc9-4b6e-87cf-41ccb477a78c/opinionated-authentication-foundation.png) --- ## The Universal Energy and the Possibility of Transformation into Personal Achievements URL: https://tgoliveira11.tech/blog/2024-10-15-universal-energy-into-personal-achievements Published: 2024-10-15 Updated: 2026-07-24 Category: Career & Reflections Tags: burnout, burnout-prevention, conservation-of-energy, energy-management, habits, high-performance, human-potential, mental-energy, mindfulness, motivation, overcoming-challenges, personal-development, personal-energy, physical-energy, productivity, purpose, self-knowledge, sustainable-performance, thermodynamics, well-being, work-life-balance Excerpt: Exploring how transforming universal energy might influence our personal success I have always been intrigued by the idea of the vital energy that moves the world and the universe. Initially, I questioned whether this universal energy could have any relation to the energy that drives us in our daily lives. Could there be a connection between the energy we need to start and complete projects and the energy that permeates the universe? And, considering that everything in the universe is governed by the laws of physics—which state that nothing is created, everything is transformed—how could this apply to our ability to overcome challenges and be more productive? Reflecting on this, I began to consider that, as human beings, we are an integral part of the universe and, therefore, subject to the same physical laws that govern all matter and energy. Biologically, our bodies function through energy transformations: the chemical energy from the food we consume is converted into usable energy to maintain our vital functions, move muscles, and sustain brain activities. These processes obey the laws of thermodynamics, especially the law of conservation of energy. Based on this, it seems possible that the energy we use to face personal challenges—whether starting a new project or overcoming obstacles—is a manifestation of the same forms of energy existing in the universe. When individuals achieve extraordinary performances, whether in sports or the corporate world, perhaps it is not because they possess more energy than others, but because they have learned to efficiently transform and direct the energy they have into concrete achievements. This perspective led me to consider that the difference may lie in the management and optimization of our personal energy. High-performing individuals seem to develop an awareness of their levels of physical and mental energy, align their goals with a clear purpose, and cultivate habits that increase energy efficiency, such as good nutrition, adequate sleep, and regular exercise. Additionally, stress management techniques and mindfulness practices can contribute to conserving mental and emotional energy. However, it is important to recognize that intensely directing our energy toward certain areas can result in imbalances in other aspects of life. Excessive focus on specific objectives can lead to burnout or neglect of personal relationships and general well-being. Therefore, sustainable management of personal energy may involve not only the pursuit of high performance but also the maintenance of a healthy balance among different areas of life. In sum, we all have access to the same basic forms of energy. The key to increasing our achievements and overcoming challenges may lie in how we transform and direct this energy. By optimizing our internal processes and aligning our actions with our purposes, we might be able to convert the available universal energy into significant personal accomplishments, respecting the fundamental laws of physics. This journey of self-knowledge and efficient energy management could be a path for us to become more productive and fulfilled, maintaining the essential balance for a full life. --- ## From Software Engineer to Engineering Director: 24 Years of Reinvention URL: https://tgoliveira11.tech/blog/2024-10-08-a-letter-to-my-past-self Published: 2024-10-08 Updated: 2026-07-24 Category: Career & Reflections Tags: ai-engineering, career-reflection, continuous-learning, engineering-leadership, entrepreneurship, software-engineering, solution-architecture, technology-leadership Excerpt: Reflections on a career shaped by software engineering, architecture, entrepreneurship, leadership, and the need to keep learning. *A letter to my past self about technology, leadership, unconventional career decisions, and the importance of remaining adaptable.* Dear me in 2002, You are about to take your first professional steps as a software engineer. You do not know where they will lead, and that uncertainty is making you question whether you are choosing the right technology, company, or career. You are looking for a clear path. I need to tell you something that will take years to understand: **There will be no clear path.** Your career will not follow a predictable sequence of promotions. It will move through software engineering, architecture, project management, entrepreneurship, executive leadership, consulting, and eventually the leadership of engineering teams building enterprise AI products. At times, that path will look inconsistent from the outside. It will make sense later. ## The First Bet Your interest in programming started before your first professional role. Your father worked as a systems analyst with large-scale mainframes, and his work made technology feel both mysterious and practical. Computers were not merely devices. They were tools capable of organizing information, automating work, and solving problems that would otherwise require substantial human effort. You will pick up a Visual Basic book with more than 500 pages, written entirely in English. You will barely understand the language. You will read it anyway. That experience will establish a pattern: you will frequently need to learn before you feel ready. In 2002, you will build your career around Microsoft technologies and the emerging .NET ecosystem. It will be useful. You will enter the market while the platform is still establishing itself in Brazil, and your knowledge will place you in projects across banking, insurance, logistics, retail, hospitality, and media. You will spend years working with languages, frameworks, databases, servers, and development tools. They will matter, but not for the reason you initially believe. The most valuable thing you will learn is not a programming language. It is how to understand a problem well enough to build something people can depend on. ## Systems Rarely Work Alone One theme will appear repeatedly: integration. You will connect applications, payment systems, authentication services, digital accounts, external platforms, and enterprise databases. The hardest problems are rarely contained inside a single application. They exist at the boundaries: - between systems; - between teams; - between technical and business requirements; - between what was designed and what operations need; - between what a product promises and what its architecture can sustain. This will move you from software development toward solution architecture. You will stop looking only at components and begin looking at relationships, constraints, failure modes, security, data flows, and long-term consequences. That transition will not happen because someone gives you a title. It will happen because the problems become larger than the code. ## The Unconventional Decision At one point, your career will appear conventional: Software Engineer. Lead Software Engineer. Solutions Architect. Project Manager. Then you will step away from that path to help build and lead a company. You will take responsibility for a B2B mobility and payment product called TecleTaxi Corporativo. An idea will need to become a working product, commercial proposition, operating company, and organization capable of supporting real customers. Technical challenges will be only part of the work. You will also think about: - product direction; - customer expectations; - contracts; - cash flow; - hiring; - team structure; - delivery commitments; - operational incidents; - investor expectations; - commercial priorities; - the consequences of a wrong decision. You will help lead a funding round and scale the organization to approximately 50 professionals across engineering, product, design, marketing, commercial, and administrative functions. This will change the way you think about engineering. As a developer, it is natural to focus on whether a solution is technically correct. As an architect, you consider whether it is scalable, secure, resilient, and maintainable. As a company leader, you must also ask: - Can the organization afford it? - Can the team operate it? - Does it solve a problem customers will pay to solve? - Can it be delivered in the available time? - What happens when it fails? - Is this the most important problem now? You will discover that technical excellence without context can become another form of waste. A sophisticated solution is not necessarily a good solution. The right solution balances customer value, risk, cost, time, organizational capability, and technical sustainability. You will not learn that from a certification. You will learn it by being accountable for the entire outcome. ## Returning to Architecture After years in executive leadership, you will return to software engineering and solution architecture. Some people may interpret that as a step backward. It will not feel that way. The technology landscape will have changed. Cloud platforms, containers, microservices, automated delivery, distributed data systems, and new operational practices will be central. You will need to catch up. That process will be uncomfortable because past experience does not eliminate the need to become a beginner again. You will study. You will rebuild technical depth. You will work closely with product, quality, data, DevOps, and engineering teams. You will design cloud-native and multi-cloud solutions. You will return to hands-on decisions with a broader understanding of business and organizational consequences. The years spent running a company will make you a different kind of architect. You will no longer evaluate architecture only through diagrams and patterns. You will consider who needs to build it, who must operate it, how quickly it can create value, and whether the organization is prepared for its complexity. Career progression is not always vertical. Sometimes the most important move is sideways. Sometimes it is back toward the work itself. ## Learning to Work Across Borders English will be difficult. There will be frustration and moments when you question whether you can communicate effectively in an international environment. Continue anyway. English will become more than another professional skill. It will provide access to people, ideas, companies, and opportunities that would otherwise remain distant. You will work with distributed teams across countries and time zones. You will learn that international collaboration requires more than vocabulary: - clear written communication; - cultural awareness; - patience; - explicit expectations; - careful documentation; - the ability to disagree without unnecessary conflict. Remote work will make geography less restrictive, but it will also expose unclear thinking. When people cannot rely on physical proximity, leaders must communicate context, decisions, and priorities with greater precision. Those hours spent learning another language will prove valuable in ways you cannot yet see. ## Another Reinvention Years later, you will lead engineering work around enterprise AI platforms. The systems will combine conversational interfaces, large language models, agent-based workflows, APIs, structured enterprise data, and cloud-native infrastructure. The technologies will be new, but many underlying problems will be familiar. Systems still need reliable integration. Data still needs ownership and governance. Users still need clear answers. Security and authorization still cannot be optional. Engineering teams still need context, standards, feedback, and room to make responsible decisions. Generative AI will not make decades of software engineering irrelevant. It will make architectural judgment even more important. A prototype can be built quickly. A dependable product still requires careful work. Continuous learning is not about chasing every technology. It is about developing enough adaptability to understand which changes matter, which principles remain valid, and which assumptions must be reconsidered. ## What I Would Tell You Now ### 1. Technical Depth Creates Options, but Judgment Creates Impact Knowing how systems work will give you credibility and independence. Knowing when to use that knowledge—and when not to—will make you effective. ### 2. Your Career Does Not Need to Be Linear Moving from engineering into management, management into entrepreneurship, and executive leadership back into architecture will not weaken your career. Each transition will add a perspective that the next role requires. ### 3. Building a Company Changes How You Evaluate Engineering Once you are responsible for customers, contracts, hiring, operations, and funding, architecture stops being an abstract technical exercise. Every decision has an organizational and economic cost. ### 4. Technologies Expire Faster Than Principles Languages, frameworks, and platforms will change. Clear thinking, modular design, security, observability, disciplined execution, and respect for users will remain important. Do not confuse attachment to a tool with professional identity. ### 5. Leadership Is Not Control You will be tempted to solve every difficult problem yourself. Resist that instinct. A leader creates clarity, builds capability, establishes standards, provides feedback, and helps others make good decisions without depending on one person for every answer. ### 6. Learning Is Part of the Job Experience will never remove the need to learn. Seniority does not mean having seen every problem. It means approaching unfamiliar problems without pretending that they are familiar. ### 7. Connections Matter—in Systems and in People Much of your technical career will involve connecting systems not designed to work together. Much of your leadership career will involve doing the same with people, teams, incentives, and objectives. Both require understanding the interfaces. ## The Path Will Make Sense Later You are beginning this journey believing that a successful career requires choosing the correct technology and following the expected progression. It does not. Technologies will change. Titles will change. Organizations will change. What will remain is curiosity, the willingness to solve difficult problems, the ability to adapt, and the desire to help teams build things that matter. You will be a developer, architect, manager, entrepreneur, consultant, and engineering director. None of those identities will replace the others. They will accumulate. The apparent detours will become part of your advantage. Keep learning. Keep questioning your assumptions. Do not be afraid to take the path that does not look like progression to everyone else. You will understand its value later. With gratitude, **Your future self** --- ## Where There's Smoke, There May Be Fire - Challenging the Simplistic View URL: https://tgoliveira11.tech/blog/2024-09-30-where-there-is-smoke-there-is-fire Published: 2024-09-30 Updated: 2026-07-24 Category: Career & Reflections Tags: analytical-thinking, assumptions, complexity, critical-thinking, decision-making, determinism, life-reflections, logic, nuance, philosophy-of-life, possibility, probability, questioning-beliefs, reasoning, smoke-and-fire, systems-thinking, truth-seeking, uncertainty, wildfires-in-brazil Excerpt: Exploring the Nuances Behind a Common Phrase and Why Reality Often Defies Simple Explanations Wildfires in Brazil have been intensifying recently, bringing images of vast areas covered in smoke. It’s almost inevitable to associate this with the popular phrase “where there’s smoke, there’s fire,” often used to suggest that when there are signs of something, there’s usually some truth behind it. But does this expression really capture the complexity of what we see? While it’s often true that smoke results from a fire, the phrase seems overly simplistic. Consider a concrete example: not all “smoke” comes from an active fire. It could be water vapor, which can easily be mistaken for smoke in certain circumstances. Or, a combustion engine generates smoke as a natural byproduct. The point is that smoke does not always equate to fire. In my daily life, working with logic and mathematics, events are calculated in terms of probabilities and possibilities. From this perspective, “where there’s smoke, there’s fire” presents a deterministic view of life, ignoring uncertainties and multiple factors that may be present. Yes, the presence of smoke often indicates fire, but there is also an important, though sometimes underestimated, margin of cases where the cause is something else. It’s important to always remember these exceptions. Life is full of nuances. Blindly believing that all smoke leads to fire ignores those subtleties. In complex situations, it is crucial to dig deeper, because simplicity often fails to reflect reality. A more fitting phrase might be “where there’s smoke, there may be fire.” This reminds us that, while it’s a possibility, it is not always the only truth. --- ## Software vs. Solution vs. System Architecture: Scope, Decisions and Accountability URL: https://tgoliveira11.tech/blog/2023-06-16-software-solution-system-architecture Published: 2023-06-16 Updated: 2026-07-24 Category: Software & Solution Architecture Tags: architecture-governance, engineering-leadership, enterprise-architecture, software-architecture, solution-architecture, system-architecture, system-design, technical-leadership Excerpt: A practical framework for distinguishing software, solution, and system architecture by the decisions each discipline owns and the outcomes for which it is accountable. *The distinction between architecture roles is not primarily about seniority. It is about scope, decision ownership, and accountability.* Software architecture, solution architecture, and system architecture are often treated as interchangeable terms. They are not. The confusion is understandable. All three disciplines deal with structure, integration, constraints, quality attributes, and technical decisions. In smaller organizations, the same person may perform all three roles. In larger companies, each title may represent a different team or governance process. The problem is that titles do not create clear boundaries. One company may call someone a Software Architect while expecting enterprise-wide integration design. Another may employ a Solution Architect whose work is limited to cloud-service selection. A System Architect may be responsible for infrastructure in one organization and a complete hardware-and-software platform in another. Rather than beginning with titles, ask: 1. What is the scope of the problem? 2. Which decisions need to be made? 3. Who is accountable for the outcome? That creates a more practical distinction. ## Architecture Is a Decision Discipline Architecture is sometimes reduced to diagrams, frameworks, technology selection, or documentation. Those are outputs. They are not the work itself. Architecture is the discipline of making consequential decisions under constraints. Constraints may include: - business objectives; - security and regulation; - existing systems; - budget; - deadlines; - team capability; - operational maturity; - performance and availability; - data ownership; - vendor limitations; - long-term maintainability. An architecture decision matters because changing it later may be expensive, risky, or disruptive. Choosing a library is usually not an architecture decision. Defining service boundaries, data ownership, authentication flows, integration patterns, deployment topology, or recovery strategies often is. The difference among software, solution, and system architecture becomes clearer through the decisions each discipline owns. ## Software Architecture Software architecture focuses on the internal structure and behavior of a software product, application, service, or platform. Typical decisions include: - application decomposition; - module and service boundaries; - domain modeling; - data ownership; - communication patterns; - consistency models; - error handling; - state management; - caching; - concurrency; - extensibility; - testing strategy; - observability; - deployment patterns; - technology selection. A Software Architect might ask: - Should this capability remain in the application or become a separate service? - Which component owns this data? - How should failures propagate? - Which operations require strong consistency? - Where should validation occur? - How can the application scale without unnecessary complexity? - How do we prevent business logic from becoming coupled to infrastructure? ### Primary Scope A software system or defined group of components. ### Primary Stakeholders Engineering, technical leads, quality, platform, and product teams. ### Primary Accountability The structural integrity and technical sustainability of the software. ## Solution Architecture Solution architecture focuses on how systems, platforms, data sources, services, and operational processes work together to solve a business problem. A solution may include custom software, existing enterprise applications, third-party products, cloud services, integration platforms, data pipelines, identity providers, and manual processes. The central question is not merely: > How should we build this application? It is: > How should the organization combine its capabilities to produce the required business outcome? Typical decisions include: - system integration; - build-versus-buy; - platform selection; - identity and access flows; - API and event strategies; - data movement and ownership; - migration; - security boundaries; - regulatory constraints; - vendor dependencies; - cross-system observability; - business continuity; - implementation roadmaps. A Solution Architect might ask: - Which systems participate in this business process? - Which source is authoritative for each data type? - How will current applications integrate with the new capability? - What should be built, purchased, reused, or retired? - How will access control remain consistent? - How can the solution be introduced without interrupting operations? - Which dependencies represent unacceptable risk? ### Primary Scope A business capability, initiative, product ecosystem, or enterprise solution involving multiple systems. ### Primary Stakeholders Business, product, engineering, data, security, infrastructure, enterprise architecture, vendors, and operations. ### Primary Accountability Ensuring that the complete technical solution addresses the business problem within its constraints. ## System Architecture System architecture focuses on the behavior and structure of the complete operational system. That system may include: - software; - cloud and physical infrastructure; - networks; - devices and sensors; - external services; - data platforms; - human operators; - support teams; - procedures; - manual fallback; - security controls. This view is especially important in industrial, financial, healthcare, telecommunications, and transportation environments. A software service may be healthy while the overall system is failing: - The application is available, but the facility network is down. - The API works, but an upstream sensor produces invalid measurements. - A transaction succeeds, but reconciliation fails. - An AI assistant generates correct SQL, but the source is stale. - Cloud infrastructure is healthy, but operators cannot access it during an incident. Typical decisions include: - system boundaries; - physical and logical topology; - availability and redundancy; - networking; - device/software interaction; - disaster recovery; - operational procedures; - monitoring; - safety controls; - capacity planning; - support models; - manual fallbacks; - system-level security; - failure containment. A System Architect might ask: - What happens when a site loses connectivity? - Which components are critical to continued operation? - How does the system degrade? - Which failures require human intervention? - How quickly must service be restored? - How will operators know that information is stale? - Are people and procedures included in the design? ### Primary Scope The end-to-end sociotechnical system, including software, infrastructure, people, process, and operational dependencies. ### Primary Stakeholders Engineering, infrastructure, security, operations, site teams, support, risk, compliance, and external providers. ### Primary Accountability Reliability, safety, operability, and end-to-end behavior under real conditions. ## A Practical Comparison | Dimension | Software Architecture | Solution Architecture | System Architecture | |---|---|---|---| | Primary focus | Internal software structure | Business capability across systems | End-to-end operational system | | Typical scope | Application, service, or platform | Initiative, ecosystem, or enterprise solution | Software, infrastructure, devices, people, and process | | Main question | How should the software be structured? | How should capabilities solve the business problem? | How should the complete system behave in operation? | | Typical decisions | Modules, services, data, consistency, frameworks | Integrations, platforms, identity, migration, build vs. buy | Topology, redundancy, networks, recovery, procedures | | Main stakeholders | Engineering teams | Business, product, engineering, security, data | Engineering, infrastructure, operations, risk, support | | Primary risk | Fragile or difficult-to-evolve software | A solution that fails the business objective | A system that fails under operational conditions | | Common artifacts | Component diagrams, ADRs, domain models | Target architecture, integration maps, roadmap | Topology, failure models, runbooks | The borders are not absolute. What matters is whether necessary decisions have a clear owner. ## A Practical Scenario Consider a platform supporting field operations. Workers use a mobile application to receive assignments, access equipment data, report incidents, and confirm work. Managers use dashboards. The platform integrates with identity, asset management, data warehouses, notification providers, and external contractors. ### Software Architecture Decisions - mobile and web interaction with backend services; - domain separation; - work-order ownership; - offline synchronization; - conflict resolution; - event publication; - API versioning; - retries; - testing and observability. ### Solution Architecture Decisions - integration with the existing asset system; - employee and contractor identity; - master-data ownership; - analytical data flow; - internal versus external notification services; - migration of legacy workflows; - build-versus-buy; - consistent authorization. ### System Architecture Decisions - loss of connectivity in remote locations; - offline capabilities; - device management; - regional cloud outage; - operational monitoring; - incident escalation; - manual fallback; - recovery without losing records. All three perspectives are necessary. The mistake is assuming one automatically covers the others. ## Where the Roles Overlap Architecture disciplines overlap because decisions are connected. A software decision may affect the solution. Asynchronous processing can improve resilience but require new reconciliation and monitoring. A solution decision may affect software. Selecting an identity platform determines authentication flows, token handling, provisioning, and boundaries. A system requirement may affect everything. Offline operation changes data storage, synchronization, conflict resolution, security, and testing. The objective is not to eliminate overlap. It is to prevent gaps and contradictory decisions. Healthy overlap involves collaboration. Unhealthy overlap involves unclear accountability. ## Accountability Matters More Than the Title A common failure occurs when an architect is expected to influence critical decisions but has no authority, access, or accountability. Another occurs when someone has the title and authority but is insulated from the consequences. Architecture governance should make five things explicit: 1. Which decisions are being made? 2. Who owns each decision? 3. Who must be consulted? 4. Which constraints cannot be violated? 5. Who accepts residual risk? An architect should not make every technical decision. That creates a bottleneck and weakens team ownership. Architectural attention should focus on decisions that are: - difficult to reverse; - cross-team; - high-risk; - security-sensitive; - operationally significant; - financially material; - dependent on long-term assumptions. Other decisions should remain close to the teams doing the work. ## Useful Architecture Artifacts Documentation should help a specific audience understand or make a decision. ### Software Architecture - context and component diagrams; - domain models; - data-ownership maps; - API contracts; - Architecture Decision Records; - sequence diagrams; - quality-attribute scenarios; - deployment models. ### Solution Architecture - business-capability maps; - current and target states; - integration and data-flow diagrams; - identity and authorization flows; - migration roadmaps; - vendor assessments; - risk and dependency registers. ### System Architecture - physical and logical topology; - failure-mode analysis; - capacity and availability models; - disaster recovery; - monitoring strategy; - operational runbooks; - manual fallback; - responsibility and escalation models. One diagram should not be expected to explain architecture to every audience. Executives, engineers, security teams, and operators need different views of the same system. ## Common Organizational Mistakes ### Treating Software Architecture as Framework Selection Languages and frameworks are only a small part of software architecture. More consequential questions concern boundaries, ownership, changeability, failure, and operation. ### Designing a Solution Without Defining the Business Outcome A coherent collection of systems is not necessarily a good solution. Architecture must connect to a measurable problem or user need. ### Ignoring Operational Reality A system does not end at the application boundary. Support, monitoring, incident response, data quality, connectivity, and human intervention are design concerns. ### Creating Architecture Without Engineering Participation Architecture imposed on delivery teams loses context and creates weak ownership. Builders and operators must participate in major decisions. ### Assigning Accountability Without Authority An architect cannot be accountable for security, reliability, cost, or integration without access to the required stakeholders and decisions. ### Over-Architecting Before Understanding Constraints Architecture should reduce meaningful risk, not add complexity to protect against hypothetical futures without evidence. ## Can One Person Perform All Three Roles? Yes. In many organizations, one experienced architect works across software, solution, and system concerns. A single architect may be sufficient when: - the product and organization are small; - the environment is understood; - integrations are limited; - operational risk is manageable; - teams have strong ownership. Specialized roles become more useful when: - multiple domains are involved; - the solution spans many systems and vendors; - physical infrastructure or devices are present; - availability and safety requirements are strict; - regulation is significant; - teams cross organizational boundaries. Role specialization should respond to complexity, not fashion. ## A Better Way to Define Architecture Roles Instead of beginning with job titles, define decision domains: - software boundaries; - systems and processes to integrate; - operational environment; - quality attributes; - critical risks; - decision owners; - parties accountable for operation. The title can follow. The accountability cannot remain ambiguous. ## Conclusion Software architecture protects the structural quality of software. Solution architecture ensures that technical capabilities work together to solve a business problem. System architecture ensures that the complete operational system—including software, infrastructure, people, and processes—behaves reliably under real conditions. None is inherently more important. They answer different questions. The quality of the result depends less on what architects are called and more on whether scope, decisions, and accountability are explicit. --- ## Securing Enterprise APIs in Production: Identity, Authorization, Abuse Prevention and Observability URL: https://tgoliveira11.tech/blog/2023-06-04-api-security Published: 2023-06-04 Updated: 2026-07-24 Category: Software & Solution Architecture Tags: abuse-prevention, api-security, authorization, identity, oauth-2, observability, openid-connect, solution-architecture, zero-trust Excerpt: A practical architecture guide to protecting enterprise APIs across human and workload identity, authorization, tenant isolation, abuse prevention, and operational visibility. *API security is not one authentication mechanism at the edge. It is a chain of decisions that must remain valid from identity to data access and audit.* API security advice is often presented as a checklist: - use HTTPS; - validate tokens; - add rate limits; - hide secrets; - place an API gateway in front of services. Every item is useful. The problem is that a checklist can create the impression that security is achieved by assembling controls. In production, most serious failures happen in the gaps between controls: a valid token used for the wrong tenant, an internal service trusted too broadly, an authorization decision enforced only at the gateway, or a sensitive operation that cannot be reconstructed after an incident. A secure API architecture needs to answer more fundamental questions: - Who or what is calling? - How was that identity established? - What is the caller allowed to do? - Which data and tenant boundaries apply? - Which downstream services may be invoked? - How is abuse detected and constrained? - Can the complete decision be audited? - What changes when an AI agent, rather than a person, initiates the request? The goal is not an “impregnable” API. No system is. The goal is a system in which trust is explicit, access is narrow, failures are contained, and important decisions are observable. ## Begin With the Type of Identity Not every caller is a human user. Enterprise APIs are commonly invoked by: - employees; - customers; - administrators; - partner applications; - background jobs; - microservices; - mobile applications; - devices; - automation platforms; - AI agents. These identities require different assurance and lifecycle models. ### Human Identity Human identity usually involves: - authentication through an enterprise or customer identity provider; - multifactor authentication where risk justifies it; - session and token lifetime; - role, group, and attribute management; - joiner, mover, and leaver processes; - account recovery; - suspicious-login detection; - consent and delegation where applicable. A person's identity is not equivalent to their current permission. Employment status, role, location, customer account, and risk context can change. ### Workload Identity Services, jobs, and agents should not impersonate generic human accounts or share static credentials. A workload identity should be: - unique; - short-lived where possible; - bound to a service or execution environment; - narrowly authorized; - rotated automatically; - attributable in logs; - revocable without affecting unrelated workloads. The distinction matters because “the backend called the API” is not a useful security boundary. Which backend? In which environment? On behalf of which user or process? With which permissions? ## Authentication and Authorization Are Different Decisions Authentication establishes identity. Authorization determines whether that identity may perform a specific action on a specific resource under current conditions. A correctly authenticated caller can still make an unauthorized request. This sounds obvious, yet systems frequently treat a valid token as sufficient access. A complete authorization decision might depend on: - subject identity; - role; - attributes; - tenant; - resource ownership; - operation; - data sensitivity; - time; - location; - device posture; - contractual relationship; - delegation; - approval state; - risk signals. The question is not: > Is this token valid? It is: > May this subject perform this operation on this resource, in this tenant, under these conditions? ## OAuth 2.0, OpenID Connect, and Tokens OAuth 2.0 is an authorization framework. OpenID Connect adds an identity layer for authentication. They solve different problems. A robust design should clearly define: - the authorization server; - resource servers; - clients; - redirect URIs; - grant types; - scopes; - audiences; - token lifetimes; - refresh-token behavior; - revocation; - signing algorithms; - key rotation; - issuer validation; - proof or sender constraints when required. A JSON Web Token is a format, not an access-control strategy. A signed JWT can still be unsafe when: - the issuer is not checked; - the audience is ignored; - the algorithm is accepted incorrectly; - keys are stale; - expiration is not enforced; - the token is replayed; - claims are trusted beyond their intended meaning; - a token issued for one service is accepted by another; - authorization is inferred from ungoverned custom claims. Token validation should be consistent across services. A centralized library, sidecar, service mesh, or platform capability may help, but ownership must remain explicit. ## Scopes, Roles, and Attributes Scopes are useful for expressing delegated capability, but they are often too broad for resource-level decisions. A scope such as `orders:read` does not answer: - which customer; - which region; - which fields; - which order status; - whether aggregated data is permitted; - whether the caller is acting for themselves or another party. Role-based access control can simplify common permissions, but roles tend to grow and overlap. Attribute-based access control can represent context more precisely, but it introduces policy complexity. Many enterprise systems need a combination: - roles for understandable job-level capabilities; - attributes for tenant, region, ownership, classification, and context; - policy rules for sensitive operations; - database controls for row and column enforcement; - application checks for business invariants. Authorization must be testable. Policies that cannot be explained or evaluated reliably become operational risk. ## Enforce Tenant Isolation at Multiple Layers In a multitenant system, a missing tenant filter can become a major breach. Tenant isolation should not depend on a client-provided identifier or a single application check. Controls can include: - tenant derived from authenticated context; - tenant-bound authorization; - row-level security; - tenant-aware data access abstractions; - separate encryption contexts; - partitioning or separate stores where risk requires it; - output validation; - cross-tenant test cases; - audit events containing tenant context. A request such as: ```http GET /customers/other-tenant-id/orders ``` must not succeed merely because the caller has a general `orders:read` scope. The resource and tenant relationship must be verified. ## The Gateway Is Important, but It Is Not the Security Boundary An API gateway can provide: - TLS termination; - token validation; - routing; - coarse authorization; - rate limiting; - request-size limits; - schema validation; - threat filtering; - logging. These controls are valuable. They do not remove the need for service-level enforcement. Internal services can be reached through alternative paths, misconfiguration, asynchronous messaging, administrative tools, or compromised workloads. Business authorization often requires domain knowledge the gateway does not have. A practical division is: ### Gateway - transport security; - trusted issuer and audience validation; - coarse policy; - quotas; - protocol and schema constraints; - request correlation; - edge-level abuse controls. ### Service - resource-level authorization; - tenant isolation; - business rules; - state-transition validation; - sensitive-field filtering; - domain-specific audit events. ### Data Layer - row and column controls where appropriate; - least-privileged database identities; - query constraints; - encryption; - immutable or protected audit records. Defense in depth is useful when each layer has a clear responsibility, not when the same ambiguous check is copied everywhere. ## Protect Secrets and Keys as Operational Assets Static API keys are sometimes appropriate for low-risk identification or controlled machine access, but they are frequently overused. Risks include: - accidental source-code commits; - logs and screenshots; - long lifetime; - shared ownership; - weak attribution; - manual rotation; - broad permissions. Prefer short-lived credentials and workload identity when the platform supports them. When secrets remain necessary: - store them in an approved secret manager; - scope them narrowly; - rotate them; - monitor their use; - avoid exposing them to build output or client code; - separate environments; - support emergency revocation; - record ownership. A secret without a lifecycle is a future incident. ## Validate More Than Syntax Input validation should cover: - structure; - type; - length; - range; - encoding; - business invariants; - allowed state transitions; - file type and size; - references to resources; - pagination limits; - sorting and filtering constraints. Schema validation is useful, but a structurally valid request may still violate business rules. For example, a payment amount may be numeric and positive but exceed the caller's authority or the contract limit. Output handling also matters. APIs may expose sensitive fields through overbroad serialization, debug information, error messages, or nested objects. Define response contracts intentionally. ## Abuse Prevention Is Different From Authentication Authenticated users and trusted clients can still misuse an API. Abuse controls may include: - rate limits; - concurrency limits; - quotas; - cost budgets; - payload limits; - query complexity limits; - pagination caps; - idempotency keys; - replay protection; - behavioral anomaly detection; - circuit breakers; - progressive restrictions; - human review for high-risk actions. A single global requests-per-second limit is rarely enough. Limits should reflect: - identity; - tenant; - operation; - computational cost; - downstream impact; - risk; - contractual entitlement. An endpoint that triggers an expensive search or financial operation should not have the same control as a lightweight health check. ## Design Safe Error Responses Errors must be useful without leaking internals. Avoid exposing: - stack traces; - database errors; - internal hostnames; - secret values; - token details; - authorization policy internals; - existence of resources the caller cannot access. Use stable error codes, correlation identifiers, and appropriate HTTP status codes. Security teams and operators need detailed internal diagnostics. External callers need enough information to respond correctly. Those are different audiences and should receive different views. ## Make Security Decisions Observable If an incident occurs, the team should be able to reconstruct: - caller identity; - workload identity; - tenant; - token issuer and audience; - authorization policy and version; - resource; - operation; - decision outcome; - gateway route; - downstream services; - data accessed; - rate-limit state; - correlation and trace identifiers; - relevant administrative changes. Do not log secrets or full tokens. Security logs themselves require access control, retention rules, integrity protection, and privacy consideration. Useful metrics include: - failed authentication; - denied authorization; - cross-tenant attempts; - abnormal token use; - quota exhaustion; - expensive-query rejection; - secret rotation failures; - unusual administrative actions; - policy-evaluation latency; - repeated access to nonexistent resources. Observability should support detection and investigation, not merely compliance reporting. ## AI Agents Expand the API Threat Model An AI agent may call APIs dynamically based on user instructions, model reasoning, retrieved data, or tool descriptions. This introduces additional questions: - Is the agent acting as itself, the user, or both? - Which tools may it use? - Which parameters may it construct? - Can untrusted content influence tool calls? - Are high-risk actions confirmed? - Is the result verified before the next action? - Are permissions narrower than the user's full access? - Can the agent access data irrelevant to the current task? - Are prompts and tool outputs treated as untrusted inputs? - Can every action be traced to an initiating user and policy decision? A safe agent design should apply: - explicit tool allowlists; - parameter validation; - least-privileged delegated tokens; - resource and tenant checks; - confirmation for irreversible actions; - policy enforcement outside the model; - bounded retries; - cost and rate limits; - complete traceability; - human escalation. The model should propose an action. Deterministic systems should decide whether the action is permitted and safe to execute. ## A Practical Security Review For each API operation, ask: 1. Who can call it? 2. How is identity established? 3. Is the identity human, workload, or delegated? 4. Which tenant and resource are involved? 5. What exact authorization policy applies? 6. Where is the decision enforced? 7. Which inputs and outputs are sensitive? 8. What abuse or cost risk exists? 9. What happens during dependency failure? 10. Which event proves what happened? 11. How is the permission revoked? 12. How would an AI agent change the risk? These questions produce a more useful architecture discussion than selecting an authentication mechanism in isolation. ## Conclusion Enterprise API security is a chain. Identity, token validation, authorization, tenant isolation, service enforcement, data controls, abuse prevention, secrets, observability, and operational response must remain consistent. A gateway can reject invalid requests. A token can establish a caller. Neither can prove that the complete business operation is safe. The objective is to make trust narrow, policy explicit, actions attributable, and failure contained. That is how APIs become dependable infrastructure rather than simply reachable endpoints. --- ## Become to Achieve - A Balanced Perspective on Corporate Culture URL: https://tgoliveira11.tech/blog/2023-05-27-become-to-achieve Published: 2023-05-27 Updated: 2026-07-24 Category: Engineering Leadership Tags: become-to-achieve, burnout-prevention, career-advancement, career-growth, continuous-improvement, corporate-culture, employee-satisfaction, employee-wellbeing, growth-mindset, healthy-workplace, leadership, learning-and-development, mental-health, mentorship, networking, organizational-culture, overwork, productivity, professional-development, recognition-and-reward, sustainable-performance, work-life-balance, workplace-culture Excerpt: Balancing Growth and Well-being - Embracing the Become to Achieve mindset in Corporate Culture Corporate culture has a significant impact on employee satisfaction and productivity. One slogan often used in organizations is “become to achieve” which advocates the importance of becoming proficient and acquiring skills before receiving promotions or opportunities. On the one hand, this slogan promotes continuous improvement and personal development. Dweck’s (2006) research on growth mindset suggests that a learning and improvement-oriented approach can contribute to career success. However, the philosophy of “become to achieve” can also lead to potential pitfalls. The constant pressure to improve can result in burnout or frustration if opportunities don’t materialize as expected. Moreover, the value of networking, mentorship and the importance of role-fit can be minimized. The issue of overwork is an additional factor that deserves attention. The “become to achieve” culture can lead to a “work more” mentality, where employees may feel obligated to demonstrate their dedication through long working hours. Research, like that conducted by Virtanen et al. (2012), links overwork to a variety of health problems, including cardiovascular diseases and mental health issues. Moreover, according to a study from Stanford University, overworking can, in fact, decrease productivity. Therefore, while continuous improvement and dedication are commendable, balance is vital. To adopt the “become to achieve” philosophy in a healthy, productive and sustainable way, here are some tips: 1. **Set clear goals**: Knowing exactly what you want to achieve can help maintain focus and motivation. 2. **Learn to learn**: Develop effective learning skills. This includes learning to learn independently, as well as staying current with the latest trends and developments in your field. 3. **Maintain balance**: While it’s important to work hard, it’s also vital to have a healthy balance between work and personal life. This can include practicing relaxation activities, physical exercise and maintaining good sleep habits. 4. **Build a support network**: Having a good support system, whether from colleagues or mentors, can be extremely beneficial. 5. **Recognize your achievements**: Don’t forget to celebrate your achievements along the way. This can help maintain motivation and self-confidence. Adopting the “become to achieve” philosophy can be a powerful strategy for personal and professional development. However, it is essential that it is adopted in a healthy, productive and sustainable way. With focus, balance and support, professionals can embrace this philosophy while taking care of their well-being and achieving their career goals. To recognize whether a company is offering a healthy “become to achieve” environment, readers can observe the following indicators: 1. **Promotion of work-life balance**: Does the company respect working hours and encourage employees to take time to rest and recharge? 2. **Learning and development opportunities**: Does the company offer training, workshops, mentorship programs, among other opportunities for employee’s professional and personal growth? 3. **Recognition and reward**: Does the company regularly recognize and reward employee efforts and achievements? 4. **Open and transparent communication**: Is the company transparent in its decisions and keeps employees informed about organizational changes? 5. **Mental health support**: Does the company have mental health support programs and encourages open discussion about employee well-being? ## Conclusion In conclusion, it’s essential for organizations to adopt a balanced approach in promoting the “become to achieve” mindset. They should encourage growth and personal development while maintaining a healthy and sustainable work culture, taking into consideration other important aspects for career advancement. --- ## Stack Overflow and AI - Evolving Together for the Future of the Tech Community URL: https://tgoliveira11.tech/blog/2023-05-19-stack-overflow-ai Published: 2023-05-19 Updated: 2026-07-24 Category: AI Engineering Tags: ai-tools, artificial-intelligence, chat-gpt, coding, collaborative-learning, community-driven-learning, continuous-learning, developer-community, developer-productivity, developer-tools, generative-ai, github, human-and-ai, innovation, knowledge-sharing, problem-solving, programming, software-architecture, software-development, software-engineering, stack-overflow, tech-community, technical-knowledge Excerpt: Understanding the Symbiosis of Community-Driven Platforms and Artificial Intelligence in Shaping the Next Generation of Problem-Solving and Innovation In the world of technology, where continuous innovation is the mantra, software engineers and architects consistently encounter complex problems. And, in this incessant cycle of creation and problem-solving, one resource has emerged as a go-to place for these professionals: Stack Overflow. ## The Tech Professionals - Architects and Software Engineers Before diving into the world of Stack Overflow, let’s take a moment to understand the roles of architects and software engineers in the tech industry. Architects, or software architects, are often responsible for the high-level structure of a system and the decision-making process involving systems design, technology stack, protocols and more. They set the vision and parameters within which the rest of the development team works. Software engineers, on the other hand, are responsible for applying engineering principles to design, develop, maintain, test and evaluate software applications and systems. They often work within the framework set by the architects, implementing features, fixing bugs and continuously iterating on the software product. ## Challenges of Tech Professionals In their day-to-day work, both architects and software engineers face challenges. Software architects often grapple with the decision-making process around technology selection, system design and the development approach. They have to think about factors like performance, scalability, security and cost. On the other hand, software engineers deal with bugs, feature implementations and ensuring the code they write fits within the framework and design set by the software architect. They often encounter issues that require deep understanding of specific technologies or languages and must solve these problems quickly and efficiently. ## The Role of Stack Overflow This is where Stack Overflow comes into play. It is a community-driven question-and-answer website, a part of the Stack Exchange network, that focuses on topics related to programming and software development. Its mission statement is simple: to **help developers learn, share their knowledge and build their careers**. But what makes it such an essential tool in the arsenal of tech professionals? ### Knowledge Base Stack Overflow has a vast repository of questions and answers accumulated over the years, making it a reliable source of knowledge for tech professionals. It’s an avenue where you can often find multiple solutions to a single problem, providing a range of perspectives and alternatives. ### Crowd-sourced Solutions Stack Overflow democratizes problem-solving. Software engineers and architects worldwide contribute their knowledge and experience to provide solutions. Therefore, the platform provides crowd-sourced wisdom, enabling tech professionals to tap into the collective intelligence of a global community. ### Real-time Assistance In addition to providing a repository of solutions, Stack Overflow serves as a real-time assistance platform. When professionals encounter unique problems, they can post their questions and community members often provide answers within minutes. This real-time problem-solving capability significantly reduces the downtime professionals might face otherwise. ### Learning and Growth Furthermore, the use of Stack Overflow transcends just problem-solving. It’s also a learning platform where professionals can improve their understanding of different technologies. By reading through different questions and answers, professionals can broaden their knowledge base, keep up with the latest trends and even prepare for job interviews. ## Recherch on Stack Overflow’s Influence Studies have revealed the depth of Stack Overflow’s impact on the tech industry. According to the 2021 Stack Overflow Developer Survey, 92% of the respondents stated that they visit Stack Overflow at least a few times a week and 74% visit Stack Overflow daily. The numbers are lower on 2022 Stack Overflow Developer Survey but still impressive once 81% of respondents visit Stack Overflow weekly and 53% of them visit daily. A study conducted by Bosu, Corley, Heaton and Chatterji in 2013 explored the role of Stack Overflow as a learning tool and found that participants learned something new in 69% of the visits and clarified their understanding in 77% of the visits. Another study by Vasilescu, Filkov and Serebrenik also in 2013 investigated the correlation between participation on Stack Overflow and productivity in GitHub projects. They found that active participants on Stack Overflow tend to be more productive on GitHub, implying the learning and problem-solving on Stack Overflow translate to practical efficiency. The study **Learning from the Crowd: Collaborative Filtering Techniques for Identifying On-the-Job Performance of Professionals** by Ming, Chen and Weinberger in 2015 focused on data from Stack Overflow and identified strong correlations between performance on the platform and on-the-job performance. ## The Bigger Picture - Stack Overflow’s Impact The tech industry moves at an astonishing pace, where keeping up with the latest trends, technologies and tools is vital. Through its vast knowledge base, Stack Overflow has emerged as a hub for both learning and problem-solving, where individuals can enhance their skills and stay updated. Software architects and engineers are crucial in this rapidly evolving industry, driving innovation and creating solutions for complex problems. Stack Overflow provides these professionals with an effective platform to share knowledge, learn from peers and troubleshoot problems in real-time. As research suggests, Stack Overflow isn’t merely a resource; it’s a lifeline for tech professionals. Its influence stretches beyond just a Q&A forum. It’s an educator, a problem solver and a catalyst for the professional growth of countless individuals in the tech industry. Its integral role in the world of software development is undeniable and likely to be even more crucial as the tech landscape continues to evolve. ## Future Predictions - Impact of AI and ChatGPT on Stack Overflow and the Tech Community The advent of artificial intelligence and advanced language models like GPT, developed by OpenAI, undoubtedly represents a turning point for the tech community and the way they seek information. These technologies have the potential to transform many aspects of our lives and it’s no different when it comes to tech professionals looking for solutions to their problems. ### The Coexistence of AI and Stack Overflow AI has a vast potential for enhancing the way tech professionals use platforms like Stack Overflow. Instead of rendering it irrelevant, AI can work to make Stack Overflow an even more potent tool. For instance, by utilizing AI, Stack Overflow can become more personalized, predicting and offering solutions to problems even before they are encountered. AI can also be used to automatically categorize and tag questions, making them easier to find and answer. Moreover, we can see a future where AI models like ChatGPT are integrated with Stack Overflow, providing real-time, automated responses to simpler queries, allowing human experts to focus on the more complex issues. This would drastically reduce the response time for many questions and enhance the user experience on the platform. ### The Evolution of Tech Community Behavior The tech community’s behavior will also evolve with the infusion of AI. Instead of manually trawling through threads and posts for solutions, they might interact with AI models directly to get solutions or get pointed in the right direction. Advanced AI models can comprehend and provide solutions to a wide range of problems, sometimes even providing an immediate response. AI is also improving in terms of generating code, meaning that future tech professionals may consult AI models for coding solutions, refactorings, or even to write whole modules or applications from scratch. This isn’t to say that all coding will be done by AI - rather, the tech community may leverage AI as a tool to enhance productivity, similar to how they use Stack Overflow today. ### Stack Overflow and the Tech Community in the AI Era Despite these advancements, it is unlikely that Stack Overflow will become irrelevant, at least in the foreseeable future. Stack Overflow thrives on the community’s collective wisdom and experience - something that AI, as of now, can’t fully replicate. While AI can provide a solution based on its training data, it lacks the ability to provide the intuitive insights that experienced tech professionals can. An architect or engineer’s nuanced advice based on personal experience, industry knowledge, or the understanding of a project’s specific context is currently irreplaceable by AI. Furthermore, the tech community is more than just a group of individuals seeking solutions to problems. It’s a platform for learning, sharing knowledge and engaging in meaningful discussions about technology trends, best practices and more. These aspects of community are far from being replaced by AI. ## Conclusion The impact of Stack Overflow on the tech community is undeniable. It serves as an irreplaceable resource for tech professionals, democratizing access to information, facilitating learning and promoting problem-solving. It’s a platform where the collective wisdom of countless tech professionals worldwide converges, allowing for the democratization of knowledge and a venue for collaboration and growth. Stack Overflow stands as a testament to the power of community-based learning and the passion of the tech community. Meanwhile, the rise of AI and specifically advanced language models like GPT, promise to revolutionize how this tech community seeks and consumes information. AI has the potential to automate, enhance and personalize many aspects of platforms like Stack Overflow, thereby transforming it into an even more potent tool. However, the advancements in AI don’t render platforms like Stack Overflow irrelevant. On the contrary, AI is likely to further complement Stack Overflow’s utility, allowing for a seamless blend of human and artificial intelligence. This means faster responses, better personalization and an overall improved user experience without sacrificing the community-driven, collaborative spirit that makes Stack Overflow unique. It’s important to note that AI, as advanced as it may be, still can’t fully replicate the nuanced insights, personal experiences and context-specific wisdom that tech professionals bring to the table. In this regard, the tech community and platforms like Stack Overflow continue to hold immense value. They remain at the heart of knowledge-sharing, problem-solving and innovation in the tech industry. In conclusion, the tech landscape is set to experience a symbiotic relationship between AI and community-driven platforms like Stack Overflow. The infusion of AI technology does not overshadow the importance of these platforms, but rather, works to enhance their value. As AI continues to evolve, so will its role in empowering the tech community, promising an exciting future for problem-solving and innovation in the tech industry. It is in this intersection of human and artificial intelligence that the future of technology lies. --- ## Architecture vs Design Patterns - An In-depth Examination of Modern Applications URL: https://tgoliveira11.tech/blog/2023-05-17-architecture-design-patterns Published: 2023-05-17 Updated: 2026-07-24 Category: Software & Solution Architecture Tags: architectural-styles, bounded-context, clean-architecture, code-reusability, ddd, decorator-pattern, design-patterns, domain-driven-design, domain-events, event-driven-architecture, factory-pattern, maintainable-code, microservices, observer-pattern, repository-pattern, scalable-systems, serverless-architecture, software-architecture, software-design, software-engineering, software-patterns, system-design, systems-design, technical-architecture Excerpt: Decoding the DNA of Software Development - A Comprehensive Guide to Architectural Styles and Design Patterns I find myself often in discussions about architecture and design patterns, two key elements that shape the way we build and comprehend software systems. Despite their inherent differences, the two terms are often confused or used interchangeably, even by experienced professionals. To bring clarity, I’d like to delve deeper into the meaning of these concepts and highlight some of the prevalent patterns and architectural styles in use today. ## Architecture and Design Patterns: A Clarification Starting with architecture, it’s best to think of it as the high-level blueprint of a software system. It provides a structured solution to meet technical and operational requirements while optimizing common quality attributes like performance, security and manageability. The architecture outlines the system’s organization, major components, their interactions, and the architectural styles and patterns used. On the other hand, design patterns are more granular, localized solutions to recurrent design problems. They are templates formulated by experienced software professionals, encapsulating proven practices that can be used to address specific issues in the context of coding and design. In essence, while architecture offers a bird’s eye view of the software structure, design patterns dive into the trenches, furnishing a reusable solution to a recurring problem within that structure. ## Exploring Major Architectural Styles 1. Microservices Architecture: Microservices architecture has become prevalent in today’s application development landscape. It involves designing a single application as a suite of small services, each running its own process and communicating through lightweight mechanisms. This architecture promotes the decoupling of components, making the system more flexible and resilient. 2. Serverless Architecture: Serverless architecture, often used with cloud computing, allows developers to build and run applications without managing servers. The server management is abstracted away by the cloud provider, allowing developers to focus more on the core functionality. This architectural style is increasingly popular due to its scalability and cost-effectiveness. 3. Event-Driven Architecture: In an event-driven architecture, the flow of the program is determined by events such as user actions, sensor outputs, or message passing from other programs. This architecture is highly adaptable and is especially useful for applications that require real-time responsiveness and scalability. 4. Domain-Driven Design (DDD): Domain-Driven Design is an approach that centers the software development around the core domain and its logic. This approach breaks down a complex system into manageable parts, known as Bounded Contexts. Each Bounded Context corresponds to a subdomain and encapsulates its complexities. The main components of DDD are Entities, Value Objects, Aggregates and Domain Events. By using a model based on the deep understanding of the business domain, DDD proves invaluable for handling complex behaviors, logic, and rules. This focus on the business domain encourages a creative collaboration between technical and domain experts, leading to software that is finely tuned to business needs. ## Examining Prominent Design Patterns 1. Factory Pattern: A creational design pattern, the Factory Method provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created. This promotes code reuse and enhances code maintainability. 2. Observer Pattern: This is a behavioral design pattern that allows objects to notify other objects about changes in their state. The Observer Pattern is widely used in GUI toolkits and event-driven systems. 3. Decorator Pattern: As a structural design pattern, the Decorator Pattern allows you to add new behaviors to objects dynamically by placing these objects inside special wrapper objects. It’s an excellent alternative to subclassing when you need to extend functionality. 4. Repository Pattern: The Repository design pattern is a popular pattern often used in conjunction with DDD. It provides a bridge between the domain and data mapping layers, using a collection-like interface for accessing domain objects. A Repository abstracts the underlying data store and provides a more object-oriented view of the persistence layer. It encapsulates all the code required to persist aggregates and entities, providing an illusion of an in-memory collection of all objects of a particular type. By using the Repository pattern, the application remains agnostic of the underlying persistence system, allowing the focus to remain on business logic rather than data access details. ## Conclusion: In conclusion, the architectural approach and design patterns we choose to implement can significantly impact the effectiveness of our software development. From high-level perspectives provided by architectures like Microservices, Serverless, Event-Driven and Domain-Driven Design, to specific, reusable solutions offered by design patterns like Factory, Observer, Decorator, and Repository, these concepts all play crucial roles. As we build and evolve our systems, understanding these roles and effectively leveraging these tools is key to creating robust, scalable, and maintainable software systems. And while it’s certainly possible and often beneficial to mix different architectures and design patterns in the same solution, it’s crucial to do so thoughtfully and judiciously, ensuring that the complexity of the system is justified by the problems it’s solving. --- ## Event-Driven Architecture & Microservices - A New Paradigm in Information Flow and Analysis URL: https://tgoliveira11.tech/blog/2023-05-12-event-driven-microservices Published: 2023-05-12 Updated: 2026-07-24 Category: Software & Solution Architecture Tags: asynchronous-communication, business-intelligence, cep, cloud-architecture, complex-event-processing, data-driven-systems, distributed-systems, eda, event-driven-architecture, event-processing, information-flow, loose-coupling, microservices, modern-architecture, real-time-analysis, service-communication, software-architecture, software-design, software-engineering, system-flexibility, system-integration, system-resilience, system-scalability Excerpt: Deciphering Event-Driven Microservices - A Journey Through Asynchronous Information Flow and Real-Time Analysis The advent of microservices and event-driven architecture has revolutionized the way we design, build and interact with software systems. These concepts have become pivotal in handling vast amounts of data and complex processes in real-time, thereby enhancing our ability to analyze and manage information flow. ## Understanding Microservices: Microservices is an architectural style that structures an application as a collection of loosely coupled services. In a microservices architecture, services are fine-grained and protocols are lightweight. This design allows developers to build and maintain each component separately, enhancing the overall agility and resilience of the application. As a result, if one microservice fails, the others will continue to work. ## Event-Driven Architecture and Microservices: Event-Driven Architecture (EDA) is a design paradigm where software components perform actions (known as event handlers) in response to receiving event notifications. In other words, instead of a central authority dictating the flow of information, components react to events as they occur. In the context of microservices, EDA can be instrumental. By using events to communicate between services, we can ensure loose coupling and high cohesion, which are hallmarks of a well-designed microservices architecture. This approach leads to more robust and flexible systems, as services can be added, modified, or removed without disrupting the overall functionality of the system. ## Information Flow: In an EDA-based microservices architecture, information flow is asynchronous and distributed. Events are published by services when a change in state occurs. Other services that are interested in these events can subscribe to them and take appropriate action. This way, each service remains unaware of others, ensuring decoupling and enabling the system to scale efficiently. ## Analysis in EDA and Microservices: In an event-driven microservices environment, real-time analysis becomes more manageable and effective. Events provide a constant flow of information representing system activity, which can be used to analyze system behavior, performance and usage patterns. For instance, imagine an e-commerce platform built on microservices. When a user places an order, it triggers a chain of events, including inventory adjustment, payment processing and shipping. By analyzing these events, businesses can gain insights into user behavior, sales trends, inventory management, and more. This approach also allows for complex event processing (CEP), where multiple events are analyzed together to detect patterns and trends. For instance, identifying a sudden surge in a specific product’s orders can indicate a change in market demand. ## Conclusion: Microservices and event-driven architecture are two paradigms reshaping how we build and interact with software systems. They offer benefits like scalability, flexibility and resilience, enhancing the system’s overall efficiency. When it comes to information flow and analysis, these paradigms offer powerful tools to understand and react to system behavior in real-time, unlocking new avenues for business intelligence and system optimization. As we continue to generate more data, such architectural designs will only grow in importance, becoming a linchpin of modern, data-driven businesses. --- ## Moving from Monolithic to Domain-Driven Design for a Distributed Web Application on Microsoft Cloud URL: https://tgoliveira11.tech/blog/2023-04-24-monolithic-to-ddd Published: 2023-04-24 Updated: 2026-07-24 Category: Software & Solution Architecture Tags: aks, asynchronous-programming, azure, azure-app-service, azure-functions, azure-stack, caching, cloud-architecture, cloud-best-practices, cloud-migration, csharp, ddd, dependency-injection, distributed-applications, distributed-architecture, domain-driven-design, dotnet-core, flexibility, hybrid-cloud, logging, maintainability, microservices, monolith-to-microservices, on-premises, platform-agnostic-architecture, same-codebase, scalability, software-architecture Excerpt: Migrating to a Domain-Driven Design approach for a Distributed Web Application that runs on both On-Premises and Cloud Environments Distributed web applications are gaining immense popularity as businesses strive to leverage the scalability, flexibility, and cost-effectiveness offered by cloud computing. However, transitioning from a monolithic architecture to a distributed architecture can pose challenges, especially when ensuring the seamless functioning of the application in both on-premises and cloud environments. In this article, we will delve into the process of migrating from a monolith to a Domain-Driven Design (DDD) approach for a distributed web application, capable of running smoothly in both on-premises and cloud settings while utilizing the same codebase. To initiate the migration towards a DDD approach for a distributed web application that can operate in both on-premises and cloud environments, the first step is to break down the monolithic application into smaller, independent services, each having its own clearly defined domain. This enables scaling and updating individual services without impacting the rest of the application. However, it’s equally important to ensure that the application can still function as a monolith in on-premises environments, even if it’s deployed as a distributed application in the cloud. To accomplish this, a platform-agnostic architecture such as microservices architecture needs to be adopted, enabling the application to run both as a monolith and as a distributed application. This ensures the utilization of the same codebase while taking advantage of the benefits offered by a distributed architecture in the cloud. Additionally, tools and frameworks compatible with both on-premises and cloud environments, such as .NET Core, Azure Functions, and Azure App Service, should be employed. To begin building a distributed web application using the DDD approach that can operate in both on-premises and cloud environments, several steps can be followed: Identify the different domains of the application: Start by identifying various functional areas within the application that can be separated into independent services, such as authentication, billing, and user management. Design the services: Once the domains are identified, meticulous design of each service is essential. Every service should possess a well-defined domain and clear boundaries with other services. Consideration should be given to how services will communicate and share data with each other. Choose the appropriate technology stack: Numerous technologies are available for building distributed web applications on the Microsoft cloud platform, including .NET Core, Azure Functions, Azure App Service, and AKS. Select the technology stack that best aligns with your specific needs while ensuring compatibility with both on-premises and cloud environments. Implement and test the services: After designing the services and finalizing the technology stack, implementation and testing of the services can commence. Each service should function independently and communicate effectively with other services. Deploy the services: Once the services are tested and ready for deployment, they can be deployed to the cloud. Adequate security measures should be in place, along with monitoring and logging capabilities to ensure smooth operation. Build a monolithic version: Following the deployment of the distributed application to the cloud, a monolithic version of the application can be created to run on-premises. This entails integrating the individual services back into a single application while maintaining clear boundaries between them. Test and deploy the monolithic version: Once the monolithic version is built, rigorous testing should be conducted to ensure its correct functioning. Subsequently, it can be deployed to the on-premises environment, utilizing the same codebase employed for the distributed application in the cloud. In summary, constructing a distributed web application on the Microsoft cloud platform using the DDD approach may present complexities, but the effort is undoubtedly worthwhile. By breaking down the monolithic application into smaller, independent services, the resulting application becomes more scalable, fault-tolerant, and easier to maintain over time. With the aid of appropriate tools and frameworks such as .NET Core, Azure Functions, Azure App Service, and AKS, you can construct a distributed web application that fulfills your requirements and helps you achieve your business objectives. ## How can I use Azure Functions and Azure App Service, for example, in a on-premises environment? Now, let’s address the question of utilizing Azure Functions and Azure App Service in an on-premises environment. While these services are primarily designed to operate in the cloud, there are several options available for their utilization in an on-premises setting. To employ Azure Functions in an on-premises environment, one option is to utilize Azure Functions Runtime. This cross-platform runtime, which is open-source, enables the execution of Azure Functions on-premises or in other non-Azure environments. With Azure Functions Runtime, you can develop, test, and run Azure Functions locally, and subsequently deploy them to an on-premises environment. Similarly, Azure App Service can be utilized in an on-premises environment through Azure App Service on Azure Stack. This extension of Azure App Service allows you to run App Service on your own hardware within your own data center. By employing Azure App Service on Azure Stack, you can develop and deploy web and mobile applications on your infrastructure while still benefiting from the features and advantages of Azure App Service. In addition to these options, other Azure services such as Azure DevOps and Azure Virtual Machines can also be utilized to build and deploy applications in an on-premises environment while integrating with Azure Functions and Azure App Service. In conclusion, while Azure Functions and Azure App Service are primarily designed for cloud deployment, there are various options available to utilize these services in an on-premises environment. This flexibility allows you to choose the deployment option that best suits your specific needs and requirements. ## \[Bonus\] Best practices coding for cloud: some key best practices for coding with C# in the cloud: 1. Embrace asynchronous programming: Asynchronous programming allows for concurrent execution of multiple operations without blocking the main thread. This can significantly enhance the performance of cloud-based applications by processing multiple requests simultaneously. 2. Implement caching: Utilize caching techniques to store frequently accessed data in memory, reducing the number of requests made to databases or other external resources. Caching solutions can be implemented using language-specific tools like the MemoryCache class in C# or distributed caching systems like Redis. 3. Leverage dependency injection: Implement dependency injection to loosely couple components, making code maintenance and testing easier. Dependency injection frameworks such as Microsoft’s built-in Dependency Injection container or third-party solutions like Autofac can assist in implementing this pattern. 4. Incorporate robust logging: Logging plays a vital role in diagnosing and troubleshooting issues within cloud-based applications. Utilize logging frameworks like the built-in logging framework or third-party solutions such as Serilog to ensure comprehensive logging capabilities. 5. Prioritize data security: Cloud-based applications are susceptible to security threats, so it’s crucial to implement security best practices. This includes encrypting sensitive data, utilizing secure authentication mechanisms, and employing security tools like the built-in Identity framework or third-party solutions such as IdentityServer. By adhering to these best practices when coding for the cloud, developers can create scalable, reliable, and secure applications that meet the demands of modern cloud environments. ## Conclusion: In conclusion, transforming a monolithic web application into a distributed web application using Domain-Driven Design principles can greatly enhance scalability, maintainability, and flexibility. By harnessing the capabilities of the cloud and adopting a distributed design approach, you can achieve greater resilience, scalability, and cost-efficiency while retaining the ability to operate your application in an on-premises environment. With the right tools and strategies, migrating to a DDD approach can be accomplished without disrupting. --- ## SOLID Principles in C# - A Guide to Writing Robust and Maintainable Code URL: https://tgoliveira11.tech/blog/2023-04-18-solid-charp Published: 2023-04-18 Updated: 2026-07-24 Category: Software & Solution Architecture Tags: abstractions, clean-architecture, clean-code, code-extensibility, code-maintainability, code-quality, csharp, customer-repository, dependency-injection, dependency-inversion-principle, design-principles, interface-segregation-principle, interfaces, liskov-substitution-principle, object-oriented-programming, oop, open-closed-principle, repository-pattern, single-responsibility-principle, software-architecture, software-design, software-development, software-engineering, solid-principles Excerpt: Learn How to Write High-Quality Code with SOLID Principles in C# - Examples Included The SOLID principle is a set of guidelines that helps software developers build code that’s simple to maintain, understand and expand. It’s an acronym for Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion principles. In this article, we’ll delve into each of these principles in detail and showcase how to put them into practice using C# programming language. ## \[S\] - Single Responsibility Principle (SRP) The Single Responsibility Principle (SRP) states that each class should have only one responsibility. In other words, a class should only be responsible for one thing. This principle makes code easier to understand and maintain, as it avoids unnecessary complexity and confusion. Let’s take an example in C# to illustrate this principle. Imagine we have a CustomerRepository class that is responsible for managing the data access for the Customer entity in a database. It has three methods: Save, Update and Delete. ``` public class CustomerRepository { private readonly DbContext _dbContext; public CustomerRepository(DbContext dbContext) { _dbContext = dbContext; } public void Save(Customer customer) { _dbContext.Customers.Add(customer); _dbContext.SaveChanges(); } public void Update(Customer customer) { _dbContext.Entry(customer).State = EntityState.Modified; _dbContext.SaveChanges(); } public void Delete(int id) { var customer = _dbContext.Customers.Find(id); _dbContext.Customers.Remove(customer); _dbContext.SaveChanges(); } } ``` In this example, the CustomerRepository class is responsible for one thing only, which is managing data access for the Customer entity. It has a clear and concise purpose, making it easier to understand and maintain. However, if we add additional responsibilities, such as calculating a customer’s total purchases or sending email notifications, then the class becomes more complex and harder to maintain. Therefore, it’s important to create separate classes for each responsibility, adhering to the Single Responsibility Principle. ## \[O\] - Open/Closed Principle (OCP) The Open/Closed Principle (OCP) states that software entities (classes, modules, functions, etc.) should be open for extension but closed for modification. In other words, you should be able to add new functionality to a system without changing existing code. Let’s continue with the example of the CustomerRepository class in C# to illustrate this principle. Suppose we want to add a new functionality that allows us to retrieve customers from a web API instead of a database. Instead of modifying the existing CustomerRepository class, we can create a new class that implements the same interface as the CustomerRepository class but uses a different data source. ``` public interface ICustomerRepository { void Save(Customer customer); void Update(Customer customer); void Delete(int id); } public class CustomerRepository : ICustomerRepository { private readonly DbContext _dbContext; public CustomerRepository(DbContext dbContext) { _dbContext = dbContext; } public void Save(Customer customer) { _dbContext.Customers.Add(customer); _dbContext.SaveChanges(); } public void Update(Customer customer) { _dbContext.Entry(customer).State = EntityState.Modified; _dbContext.SaveChanges(); } public void Delete(int id) { var customer = _dbContext.Customers.Find(id); _dbContext.Customers.Remove(customer); _dbContext.SaveChanges(); } } public class ApiCustomerRepository : ICustomerRepository { private readonly HttpClient _httpClient; public ApiCustomerRepository(HttpClient httpClient) { _httpClient = httpClient; } public void Save(Customer customer) { throw new NotImplementedException(); } public void Update(Customer customer) { throw new NotImplementedException(); } public void Delete(int id) { throw new NotImplementedException(); } } ``` In this example, we created a new class called ApiCustomerRepository that implements the ICustomerRepository interface. We can now use this class to retrieve customers from a web API instead of a database without modifying the existing code. This adheres to the Open/Closed Principle, as we are able to extend the functionality of the system without changing the existing code. ## \[L\] - Liskov Substitution Principle (LSP) The Liskov Substitution Principle (LSP) states that if a program is using a base class, it should be able to use any of its derived classes without knowing it. In other words, a derived class should be substitutable for its base class without causing errors or unexpected behavior. Continuing with the example of the CustomerRepository class in C#, let’s suppose we have a derived class called InMemoryCustomerRepository that stores customer data in memory instead of a database. This class should be substitutable for the base CustomerRepository class, as they both implement the ICustomerRepository interface. ``` public class InMemoryCustomerRepository : ICustomerRepository { private List _customers = new List(); public void Save(Customer customer) { _customers.Add(customer); } public void Update(Customer customer) { var existingCustomer = _customers.FirstOrDefault(c => c.Id == customer.Id); if (existingCustomer != null) { existingCustomer.Name = customer.Name; } } public void Delete(int id) { var customer = _customers.FirstOrDefault(c => c.Id == id); if (customer != null) { _customers.Remove(customer); } } } ``` In this example, the InMemoryCustomerRepository class is substitutable for the CustomerRepository class, as it implements the same ICustomerRepository interface and provides the same functionality. We can now use either class interchangeably without causing errors or unexpected behavior, adhering to the Liskov Substitution Principle. ## \[I\] - Interface Segregation Principle (ISP) The Interface Segregation Principle (ISP) states that classes should not be forced to implement interfaces they don’t use. In other words, a class should only depend on the interfaces that it actually needs and not on any additional ones that it doesn’t. Let’s continue with the example of the CustomerRepository class in C# to illustrate this principle. Suppose we have another interface called IOrderRepository that defines methods for managing orders in a database. ``` public interface IOrderRepository { void Save(Order order); void Update(Order order); void Delete(int id); } ``` If we were to implement this interface in the CustomerRepository class, it would violate the ISP because the CustomerRepository class doesn’t need to manage orders. Instead, we can split the ICustomerRepository interface into smaller interfaces, each with its own set of methods. ``` public interface ICustomerSaver { void Save(Customer customer); } public interface ICustomerUpdater { void Update(Customer customer); } public interface ICustomerDeleter { void Delete(int id); } public interface ICustomerRepository : ICustomerSaver, ICustomerUpdater, ICustomerDeleter { // Methods specific to customer repository go here } ``` In this example, we split the ICustomerRepository interface into three smaller interfaces that each define a single responsibility. The ICustomerRepository interface then inherits from these smaller interfaces. This adheres to the Interface Segregation Principle because each class can now depend only on the interfaces that it actually needs, without being forced to implement any additional methods that it doesn’t need. ## \[D\] - Dependency Inversion Principle (DIP) The Dependency Inversion Principle (DIP) states that high-level modules should not depend on low-level modules, but both should depend on abstractions. In other words, classes should depend on abstract interfaces rather than concrete implementations. To illustrate this principle using the CustomerRepository class in C#, let’s suppose that the CustomerService class depends on the concrete CustomerRepository class to access the database. ``` public class CustomerService { private CustomerRepository _repository; public CustomerService() { _repository = new CustomerRepository(); } public void Save(Customer customer) { _repository.Save(customer); } public void Update(Customer customer) { _repository.Update(customer); } public void Delete(int id) { _repository.Delete(id); } } ``` In this example, the CustomerService class is directly dependent on the CustomerRepository class, which violates the DIP. To adhere to the DIP, we should introduce an abstraction between the CustomerService and CustomerRepository classes. ``` public interface ICustomerRepository { void Save(Customer customer); void Update(Customer customer); void Delete(int id); } public class CustomerService { private ICustomerRepository _repository; public CustomerService(ICustomerRepository repository) { _repository = repository; } public void Save(Customer customer) { _repository.Save(customer); } public void Update(Customer customer) { _repository.Update(customer); } public void Delete(int id) { _repository.Delete(id); } } ``` In this modified example, we introduced an interface called ICustomerRepository that the CustomerRepository class now implements. The CustomerService class is now dependent on the abstraction, rather than the concrete implementation of the repository. This adheres to the DIP because the CustomerService class now depends on an abstraction, rather than a concrete implementation. ## Conclusion: In conclusion, the SOLID principles serve as invaluable guidelines for software development, placing a strong emphasis on creating code that is effortless to maintain, comprehend, and expand. By adhering to these principles, developers can craft software that is adaptable, resilient, and readily adaptable to change. Throughout this article, we delved into each of the SOLID principles, providing in-depth explanations and illustrating their implementation with practical code examples in C#. The Single Responsibility Principle (SRP) highlights the significance of ensuring that each class focuses on a single responsibility. To illustrate this principle, we examined the case of a Customer class that exclusively handled customer data storage, exemplifying the benefits of a well-defined and focused responsibility. The Open/Closed Principle (OCP) urges developers to construct code that is open to extension while remaining closed to modification. We showcased this principle through a CustomerRepository class, which harnessed the power of interfaces and abstract classes to enable seamless extension without the need for modifying existing code. The Liskov Substitution Principle (LSP) underscores the importance of maintaining interchangeability between derived classes and their base classes. Our exploration of this principle revolved around a CustomerRepository class, which impeccably adhered to the contract defined by its base class, allowing for effortless substitution and preserving the expected behavior. The Interface Segregation Principle (ISP) posits that classes should not be compelled to implement interfaces that they don’t require. To exemplify this principle, we examined a CustomerRepository class that intelligently partitioned its interface into smaller, more focused interfaces, avoiding the imposition of unnecessary methods on implementers. Lastly, the Dependency Inversion Principle (DIP) stresses the significance of depending on abstractions rather than concrete implementations. We shed light on this principle through a CustomerService class that relied on the abstraction provided by the ICustomerRepository interface, demonstrating the advantages of decoupling code from specific implementations. By embracing these SOLID principles, developers gain the ability to write code that is not only flexible and extensible but also effortlessly adaptable to change. By upholding these principles, software projects become a breeze to maintain, comprehend, and enhance, ultimately resulting in a robust and agile codebase that can gracefully accommodate evolving requirements over time. --- ## Understanding Dependency Injection in Modern Coding URL: https://tgoliveira11.tech/blog/2023-03-14-di-modern-coding Published: 2023-03-14 Updated: 2026-07-24 Category: Software & Solution Architecture Tags: abstractions, clean-architecture, clean-code, code-flexibility, code-maintainability, csharp, ddd, dependency-injection, dependency-inversion-principle, dependency-management, design-patterns, domain-driven-design, dotnet, dotnet-core, interfaces, loose-coupling, microservices, mocking, modularity, moq, service-collection, single-responsibility-principle, software-design, software-engineering, solid-principles, testability, unit-testing Excerpt: Benefits, Drawbacks and Implementation of Dependency Injection with code examples in C# Dependency Injection (DI) is a programming concept that aims to foster loose coupling between components by separating the creation of dependencies from their usage. In simpler terms, instead of a component creating its own dependencies, it relies on an external entity to provide them. This external entity, often a container or framework, takes charge of managing dependencies. In practice, this means that rather than hardcoding dependencies within a class or method, the dependencies are passed as parameters to the class or method. This approach enhances the reusability, testability, and flexibility of the class or method. Moreover, it upholds the Single Responsibility Principle (SRP) by enabling components to focus on their core responsibilities without being burdened with dependency creation or management. For instance, let’s consider a class responsible for sending emails. Without DI, the class might create its own instance of an email service, resulting in a tight coupling between the two components. However, with DI, the email service can be injected into the class as a dependency, promoting modularity and reducing interdependencies. Embracing DI in modern coding brings several benefits: 1. Testability: DI simplifies the process of writing unit tests since dependencies can be easily mocked or substituted with test-specific implementations. 2. Flexibility: DI facilitates runtime substitution of dependencies, allowing for dynamic implementation changes without impacting the functionality of the code. 3. Readability: DI enhances code readability by making it more modular and easier to comprehend. 4. Maintainability: By reducing coupling and promoting modularity, DI makes it easier to maintain the codebase. In summary, DI is a fundamental concept in modern coding that fosters decoupling and modularity in code components. It empowers developers to build code that is more testable, flexible, readable, and maintainable. While dependency injection offers significant advantages, it’s important to consider potential disadvantages: 1. Complexity: Dependency injection can introduce additional layers of abstraction and indirection, potentially making the code more complex and challenging to understand or debug, particularly for developers unfamiliar with the codebase or the specific DI framework being used. 2. Over-engineering: Excessive use of dependency injection can lead to over-engineering, where unnecessary abstractions and dependencies are created, needlessly complicating the codebase without providing substantial benefits. 3. Performance: Depending on the specific DI framework employed, there might be some performance overhead associated with using dependency injection. This is because the DI framework needs to instantiate objects and manage dependencies at runtime, which can introduce some additional processing compared to direct object instantiation. 4. Configuration: Configuring the dependency injection framework can be a complex task, especially in larger applications with numerous dependencies. This complexity can result in configuration errors that are challenging to diagnose and rectify. Overall, while there are potential disadvantages to using dependency injection, they can generally be mitigated through careful design and implementation. By adhering to best practices and employing a well-designed DI framework, developers can harness the benefits of dependency injection without encountering significant drawbacks. Now, let’s take a look at an example of how dependency injection can be used in C# code. ``` // Define an interface for the dependency public interface IMessageService { void SendMessage(string message); } // Define an implementation of the interface public class EmailService : IMessageService { public void SendMessage(string message) { // Send an email with the message } } // Define a class that depends on the interface public class MyController { private readonly IMessageService _messageService; public MyController(IMessageService messageService) { _messageService = messageService; } public void DoSomething() { // Use the message service to send a message _messageService.SendMessage("Hello, world!"); } } // Set up dependency injection var serviceProvider = new ServiceCollection() .AddSingleton() .BuildServiceProvider(); // Use the dependency in the controller var controller = serviceProvider.GetService(); controller.DoSomething(); ``` Let’s explore an example that demonstrates the power of dependency injection. In this scenario, we have an interface called IMessageService, which represents a dependency needed by our MyController class. Additionally, we have implemented the interface with EmailService, a class responsible for sending emails containing a specific message. Within our MyController class, we embrace the concept of dependency injection by injecting an instance of IMessageService through the constructor. This elegant approach allows us to leverage any implementation of IMessageService without making any modifications to the MyController class itself. To configure dependency injection, we utilize the ServiceCollection class. We add a singleton instance of EmailService as the implementation for IMessageService. Once the setup is complete, we retrieve an instance of MyController from the service provider. We can then call the DoSomething() method on the MyController instance, which internally uses the EmailService implementation to send a message. By embracing dependency injection in this manner, we unlock the ability to write code that is highly flexible and easily testable. The beauty lies in the fact that we can effortlessly substitute dependencies without the need to make changes to the classes that rely on them. Now, let’s delve into an illustrative example of a unit test for the IMessageService interface. ``` public class MessageServiceTests { [Fact] public void SendEmail_ValidEmail_ReturnsTrue() { // Arrange var mockEmailSender = new Mock(); mockEmailSender.Setup(s => s.SendEmail("[email protected]", "Test email", "This is a test email.")).Returns(true); var messageService = new MessageService(mockEmailSender.Object); // Act var result = messageService.SendEmail("[email protected]", "Test email", "This is a test email."); // Assert Assert.True(result); } } ``` In this example, we’ll explore how dependency injection can greatly benefit our unit testing process. We’ll utilize the Moq mocking framework, which allows us to create a mock object of the IEmailSender interface. With this mock object, we can configure its behavior to return a specific value when its SendEmail method is called with certain arguments. By employing dependency injection, we pass this mock object to the constructor of the MessageService class. Now, let’s focus on the SendEmail\_ValidEmail\_ReturnsTrue test method. Within this method, we aim to test the SendEmail functionality of the MessageService class. We provide valid email parameters and expect the method to return true. By employing the Assert statement, we verify that the actual result aligns with our expected outcome. By utilizing dependency injection in our unit test, we gain the ability to effortlessly mock the IEmailSender dependency. This approach allows us to test the MessageService class in isolation, without relying on a real implementation of IEmailSender. As a result, our test becomes more reliable, enabling us to quickly identify and resolve any issues that arise. In the realm of .NET Core, the built-in dependency injection container is configured through the use of the IServiceCollection interface. This interface equips us with methods to register services and dependencies, and later resolve them as needed. Depending on the application’s requirements, the container can be configured with various lifetime scopes such as singleton, scoped, or transient. The advantage of employing dependency injection in .NET Core lies in its ability to simplify dependency management and facilitate smoother unit testing. Rather than manually creating and managing dependencies, the container automatically resolves them and injects them into classes and components. This approach fosters modular code that is easier to maintain and more adaptable to change. .NET Core effectively addresses potential drawbacks associated with dependency injection. Here are a few examples: 1. Reduction of boilerplate code: Dependency injection frameworks like Microsoft.Extensions.DependencyInjection in .NET Core offer convenience and utility by streamlining the registration and resolution of dependencies, eliminating the need for excessive boilerplate code. 2. Improved testability: With .NET Core’s dependency injection framework, swapping dependencies with mock objects during testing becomes a breeze. This simplifies the isolation and testing of individual components, enhancing the overall testability of the codebase. 3. Enhanced modularity and maintainability: .NET Core’s dependency injection framework allows for the development and testing of components in isolation, improving code maintainability. It becomes easier to update individual components without affecting the entire system. Overall, .NET Core’s built-in dependency injection framework presents a simple and efficient approach to dependency management in your application, addressing common challenges associated with manual dependency handling. When combined with other design patterns such as microservices, DDD (Domain-Driven Design), SOLID, and Clean Architecture, dependency injection becomes a powerful tool that significantly elevates the overall quality and maintainability of a .NET Core application. It reinforces the separation of concerns and minimizes coupling between components. By adhering to these principles, developers can create scalable and maintainable software that is easily adaptable, extendable, and well-tested. --- ## Securing Your Web Application - The Importance of SAST and DAST Tests on Azure DevOps URL: https://tgoliveira11.tech/blog/2023-02-10-sast-dast Published: 2023-02-10 Updated: 2026-07-24 Category: Software & Solution Architecture Tags: access-control, application-security, authentication-security, azure-dev-ops, burp-suite, continuous-security, csharp, csrf, cybersecurity, dast, data-breaches, dev-sec-ops, dynamic-application-security-testing, owasp-zap, remote-code-execution, roslyn-security-guard, sast, secure-sdlc, security-pipeline, security-testing, sonar-qube, sql-injection, static-application-security-testing, vulnerability-detection, web-applications, web-security, xss Excerpt: Implementing Effective Security Testing in Azure DevOps Have you ever come across SAST and DAST security tests? Well, let me tell you, these tests are absolutely crucial when it comes to ensuring the security of web applications. With the web playing an increasingly vital role in our daily lives, the security of web applications has become a paramount concern for both businesses and individuals. The repercussions of a security breach can be far-reaching, ranging from financial losses and damage to reputation to the theft or leakage of sensitive data. Let’s take a look at some common risks associated with security breaches: 1. Data breaches: This involves the theft or unauthorized access to sensitive information like personal data, financial records, or intellectual property. 2. Authentication and access control vulnerabilities: Weaknesses in user authentication and authorization mechanisms can lead to unauthorized access to sensitive information or enable attackers to elevate their privileges. 3. Cross-Site Scripting (XSS): In this scenario, attackers inject malicious scripts into web pages, which are then executed by unsuspecting users. This can result in the theft of sensitive information such as passwords and cookies, or even grant attackers control over the user’s session. 4. SQL Injection: Attackers exploit vulnerabilities by injecting malicious SQL code into an application. This can lead to unauthorized access to sensitive data or allow manipulation or deletion of data. 5. Cross-Site Request Forgery (CSRF): Attackers trick users into unknowingly making unintended requests to a web application. This can result in unauthorized transactions, such as changing passwords or making unauthorized purchases. 6. Remote Code Execution (RCE): Attackers exploit vulnerabilities in web applications to execute arbitrary code on the server, gaining complete control over the compromised system. To mitigate these risks, it is crucial to adopt robust security testing practices. Static Application Security Testing (SAST) is one such practice that involves analyzing the source code for vulnerabilities before deploying the application. On the other hand, Dynamic Application Security Testing (DAST) focuses on testing the application for vulnerabilities during runtime. Let’s explore the advantages of each approach: ## Advantages of SAST: * Early detection: SAST enables the identification of security issues early in the development process, allowing for prompt resolution before deploying the application. This significantly reduces the risk of a security breach. * Automation: SAST tools are typically automated, facilitating efficient security testing of large applications within a relatively short period of time. * Cost-effectiveness: SAST offers a cost-effective way to assess the security of your applications, as it can be performed without any interaction with the runtime environment. ## Advantages of DAST: * Real-world testing: DAST conducts tests during runtime, providing a more realistic assessment of your application’s security by observing its behavior in action. * Identification of runtime vulnerabilities: Unlike SAST, which focuses on the source code, DAST can uncover security vulnerabilities that may only manifest during runtime. * Complement to SAST: DAST and SAST work in harmony, providing a comprehensive view of your web application’s security posture. ## Implementing a Good Security Testing Program on Azure DevOps: Now, let’s delve into implementing these tests within an Azure DevOps pipeline for a C# web application. The initial step involves setting up a build pipeline in Azure DevOps. Within this pipeline, a task is included to run a SAST tool such as Roslyn Security Guard or SonarQube. This tool meticulously analyzes the application’s source code to identify any security vulnerabilities. Once the build pipeline is complete, the focus shifts to the release pipeline. Here, a task is incorporated to execute a DAST tool like OWASP ZAP or Burp Suite. These tools simulate an attacker attempting to exploit the application and provide reports on any vulnerabilities detected. By integrating SAST and DAST tests into our Azure DevOps pipeline, we ensure that security becomes an integral part of the development process right from the start. This approach helps prevent the introduction of security vulnerabilities into the application and enables the identification and remediation of any existing vulnerabilities. Ultimately, it leads to the creation of a more secure and trustworthy application. Monitoring the results of your security tests is crucial. It’s essential to keep a close eye on the findings and take prompt action to address any identified issues. This may involve fixing vulnerabilities or updating your security tests to cover emerging threats. Remember, security is an ongoing process. Continuous improvement of your security testing program is vital to stay ahead of potential risks. This may entail integrating new tools, updating existing tests, or adjusting your testing process to adapt to evolving security challenges. In conclusion, security testing plays a vital role in safeguarding web applications. SAST and DAST tests are essential components of a comprehensive security strategy. By implementing these tests within an Azure DevOps pipeline, you can ensure that security remains a top priority throughout the development lifecycle. Stay vigilant, continuously improve, and make security an integral part of your web application development process. --- ## Assessing the Critical Role of Solution Architects in Modern Business URL: https://tgoliveira11.tech/blog/2023-01-11-assessments Published: 2023-01-11 Updated: 2026-07-24 Category: Software & Solution Architecture Tags: aadl, adr, apm, architecture-assessment, architecture-decision-records, architecture-patterns, atam, bpmn, business-alignment, business-requirements, distributed-tracing, enterprise-architecture, load-testing, model-driven-engineering, penetration-testing, performance-assessment, scalability, security-assessment, siem, software-architecture, software-solutions, solution-architect, solution-architecture, stakeholder-collaboration, system-design, technical-leadership, technology-assessment, threat-modeling, use-cases, user-stories, vulnerability-scanners Excerpt: An Overview of Modern Tools and Skills Required for a Successful Solution Architect - From Software Architecture to Performance Assessment In today’s rapidly evolving business landscape, technology plays an increasingly crucial role, underscoring the growing importance of Solution Architects. These professionals are responsible for designing software solutions that effectively meet the specific needs of businesses while ensuring scalability, security, and efficiency. Succeeding in this role requires a diverse skill set and the ability to conduct various assessments. Among the crucial assessments performed by Solution Architects, evaluating software architecture stands out as a primary focus. This assessment entails a comprehensive examination of the software system’s structure, including component interactions, with the goal of identifying areas for improvement. Additionally, it involves assessing the system’s scalability, security, and performance to guarantee alignment with the business’s requirements. In modern practice, several tools facilitate software architecture assessments. Here are a few notable examples: 1. Architecture Decision Records (ADR): ADRs serve as a documentation method to capture and record decisions made during the architecture assessment process. This practice aids in maintaining consistency with the original vision when implementing future changes. 2. Architecture Analysis and Design Language (AADL): AADL is a modeling language specifically designed for describing software architectures. It enables architects to visualize the system’s structure and assess its scalability, safety, and security. 3. Architecture Tradeoff Analysis Method (ATAM): ATAM is a method used to evaluate software architectures based on their quality attributes. It involves identifying tradeoffs between different attributes and selecting the most suitable architecture based on the specific system needs. 4. Architecture-centric Virtual Integration Process (AVIP): AVIP is a process employed to test and validate software architectures. By simulating the system and its components, AVIP ensures their seamless integration and proper functionality. 5. Architecture Patterns: Architecture patterns provide best practices and guidelines for designing software architectures. They offer a framework that promotes scalability, maintainability, and security in system design. 6. Model-Driven Engineering (MDE): MDE is an approach to software development that utilizes models to design, develop, and test software systems. It allows architects to create models of the system and assess its behavior before the actual implementation. In addition to assessing software architecture, Solution Architects must possess the ability to evaluate technology options. Staying up-to-date with the latest trends, they need to assess available technologies and select the most appropriate solutions for specific business needs. This evaluation involves considering scalability, security, performance, and cost factors of available technologies. Another critical assessment performed by Solution Architects pertains to business requirements. To successfully translate business needs into technical requirements, Solution Architects must understand the organization’s objectives and the expectations of end-users. This assessment ensures that the proposed solution aligns with the end-users’ needs and addresses the desired outcomes. Multiple tools are available to facilitate the assessment of business requirements. Here are a few examples: 1. Business Process Model and Notation (BPMN): BPMN is a modeling language used to represent business processes. It enables analysts to visualize the flow of activities and identify areas for improvement. 2. User Stories: User stories are concise descriptions of features or functionalities required by end-users. They serve as a tool to capture user needs effectively. 3. Acceptance Test-Driven Development (ATDD): ATDD is an approach to software development that involves defining test cases based on user stories. It allows teams to validate that the software meets the specified requirements. 4. Business Rules: Business rules consist of guidelines and constraints that govern the behavior of a system. They ensure that the software aligns with business requirements. 5. Use Cases: Use cases provide detailed descriptions of user-system interactions. They help identify the primary functionalities of the system and ensure they meet business requirements. 6. Business Capability Maps: Business Capability Maps assist in identifying the capabilities needed by the business. They ensure that the software meets the business’s requirements and aids in achieving its goals. Evaluating security is a vital aspect of a Solution Architect’s role. They have the responsibility to thoroughly assess the security of proposed solutions, ensuring they meet the company’s stringent security standards. This involves identifying potential vulnerabilities and taking necessary steps to minimize risks. In the realm of security assessment, there exist various modern tools that can assist Solution Architects. Here are a few examples: 1. Vulnerability Scanners: These automated tools scan systems meticulously, actively searching for known vulnerabilities. They excel at uncovering issues like missing patches, configuration errors, and outdated software, which can serve as potential gateways for security breaches. 2. Penetration Testing: This assessment method involves simulating attacks on a system to unveil vulnerabilities. Penetration testing can be performed manually or with the aid of automated tools, enabling the discovery of weaknesses that malicious actors might exploit. 3. Security Information and Event Management (SIEM): SIEM systems play a crucial role in collecting and analyzing security-related data from diverse sources, such as network devices, servers, and applications. By diligently monitoring and correlating security events, they act as an early warning system, promptly detecting and alerting security teams about potential threats. 4. Intrusion Detection and Prevention Systems (IDPS): These systems act as vigilant guards, continuously monitoring network traffic for any signs of suspicious activity. They possess the ability to detect and prevent intrusions into a system, serving as a vital defense mechanism against unauthorized access and safeguarding sensitive information. 5. Security Configuration Management: Security Configuration Management tools aid in managing and enforcing security policies throughout an organization. They ensure that systems are appropriately configured and comply with the highest security standards, reducing the risk of misconfigurations that may lead to vulnerabilities. 6. Threat Modeling: Threat modeling is an essential process used to identify potential threats to a system and determine effective mitigation strategies. It involves in-depth analysis of the system’s architecture, data flows, and possible attack vectors, enabling architects to proactively identify weaknesses and implement appropriate security measures. Additionally, evaluating performance is an integral aspect of a Solution Architect’s role. They must ensure that the proposed solution meets the performance requirements of the business. This evaluation includes assessing scalability and pinpointing potential performance bottlenecks. To assess software performance, there are several modern tools available: 1. Load Testing Tools: These tools simulate heavy traffic on a system to gauge its performance under various load conditions. By identifying performance bottlenecks, they contribute to optimizing system performance and ensuring it can handle anticipated workloads effectively. 2. Profiling Tools: Profiling tools delve into the software code to identify performance issues. They excel at pinpointing functions that consume excessive resources, identifying memory leaks, and highlighting other performance-related concerns. 3. Application Performance Management (APM) Tools: APM tools provide real-time monitoring of application performance, enabling the identification of issues and bottlenecks as they occur. They offer valuable insights into the root causes of performance problems and facilitate the optimization of system performance. 4. Distributed Tracing Tools: These tools offer the ability to trace the path of a request through a distributed system. By pinpointing where bottlenecks occur, architects can optimize performance and ensure seamless interactions between different system components. 5. Metrics Collection and Analysis Tools: These tools collect and analyze performance data, providing valuable insights into system performance. They track metrics such as CPU usage, memory consumption, and response times, allowing for the identification of performance issues and trends. 6. Real User Monitoring (RUM) Tools: RUM tools capture data on how real users interact with the system, offering valuable insights into performance issues that may not be evident through traditional performance testing. They help ensure an optimal user experience. It’s worth emphasizing that while automated tools can provide valuable assistance, they should not be solely relied upon. A comprehensive assessment requires a balanced combination of automated and manual methods to ensure thoroughness and accuracy. Solution Architects often collaborate closely with various teams and stakeholders, including developers, quality assurance teams, project managers, and business stakeholders. This collaboration allows them to gather essential information and conduct assessments effectively. Additionally, Solution Architects may need to engage with other architects, such as Enterprise Architects, to ensure their proposed solutions align with the organization’s broader IT strategy. To work harmoniously with diverse teams and stakeholders, effective communication and collaboration skills are paramount for Solution Architects. They must possess the ability to articulate technical concepts in a manner that is easily understandable by non-technical individuals. In addition to communication and collaboration skills, Solution Architects must demonstrate strategic thinking capabilities. They must assess an organization’s business needs and goals and align their proposed solutions accordingly. This necessitates a deep understanding of the organization’s IT strategy and a willingness to adapt their approach to meet specific business requirements. In conclusion, the role of a Solution Architect is pivotal in today’s technology-driven business landscape. Success in this role hinges on the ability to perform comprehensive assessments encompassing software architecture, technology options, business requirements, security, and performance. By doing so, Solution Architects can design software solutions that effectively address the business’s needs, while also emphasizing scalability, security, and efficiency. Furthermore, Solution Architects must remain adaptable and committed to continuous learning and growth, as technology and business needs evolve over time. By staying ahead of the curve, they can continue to craft innovative and effective solutions that meet the dynamic needs of the business. --- ## Implementing and Monitoring Dockerized Microservices in Kubernetes on Azure - A Case Study of a Global Taxi App URL: https://tgoliveira11.tech/blog/2022-12-14-monitoring-microservices Published: 2022-12-14 Updated: 2026-07-24 Category: Software & Solution Architecture Tags: aks, application-monitoring, asynchronous-messaging, azure, azure-application-insights, azure-event-hubs, azure-kubernetes-service, azure-monitor, azure-service-bus, cloud-architecture, cloud-monitoring, container-orchestration, containerization, data-analysis, dev-ops, distributed-systems, docker, event-driven-architecture, k8s, kql, kubernetes, kusto-query-language, log-analytics, microservices, observability, performance-monitoring, power-bi, stream-analytics, system-reliability, system-resilience, system-scalability, telemetry-data Excerpt: Unlocking Performance Insights - Effective Monitoring and Analysis of Dockerized Microservices in Kubernetes on Azure The current era of cloud computing has opened up new possibilities for system design, with distributed microservices architectures leading the way. These architectures allow for services to be decoupled, thus promoting better scalability, flexibility and resilience. When paired with event-driven and asynchronous messaging patterns, highly efficient systems capable of managing significant concurrent workloads can be built. In this article, we will explore how Docker and Kubernetes (K8s) fit into this architecture, with a focus on monitoring and data analysis in an Azure environment. To offer practical examples, we will use a hypothetical global taxi application. ## 1\. Docker, Kubernetes and Microservices Architecture In our global taxi app, we employ a distributed microservices architecture. Each service, such as user management, ride booking, or payment processing, is an individual unit. This separation ensures that each service can scale independently and failures remain isolated. In this scenario, Docker and Kubernetes play crucial roles. Docker provides a way to package and distribute software in containers, ensuring consistency across environments. Kubernetes, on the other hand, manages these Docker containers, handling deployment, scaling and automatic failover. Each service in our taxi app is containerized using Docker, allowing for consistency, reproducibility and isolation. Kubernetes manages these containers, orchestrating them based on the needs of the system. The app also uses an event-driven approach. Changes such as a new ride booking trigger events, which are listened for by other relevant services (like the driver allocation service). These messages are sent asynchronously, ensuring the sender doesn’t have to wait for a response before proceeding with other tasks, thus improving overall system efficiency. ## 2\. Monitoring Dockerized Microservices in Kubernetes on Azure Microsoft Azure provides robust tools for monitoring microservices running in Kubernetes. Azure Monitor for containers and Azure Application Insights offer deep insights into the performance and usage of our services. ### 2.1 Azure Monitor for Containers Azure Monitor for containers provides a real-time view into your Azure Kubernetes Service (AKS), enabling you to monitor the performance of workloads, understand trends and diagnose issues. It allows you to track the health of our taxi app, monitoring metrics like CPU usage, memory usage, network usage, etc., for each microservice. ### 2.2 Azure Application Insights Azure Application Insights is an extensible Application Performance Management (APM) service for developers and DevOps professionals. It helps monitor live applications, detect performance anomalies and diagnose issues with distributed services. ## 3\. Effective Monitoring: Events, Messages and Logs ### 3.1 Monitoring Events In our taxi app, events such as user sign-ups, ride bookings, or payments trigger asynchronous messages. Azure Event Hubs can stream this event data in real-time. When paired with Azure Stream Analytics, we can process and analyze incoming data on-the-fly and respond promptly to any critical changes. ### 3.2 Monitoring Messages Monitoring the messages between services is essential for tracking system flow and identifying bottlenecks. Azure Service Bus, a fully managed enterprise message broker, is ideal for this. It provides real-time data about the messages, such as message count, active messages and dead-letter messages, which can be visualized in Azure Monitor. ### 3.3 Monitoring Logs Logs provide vital information about system operations and any issues that might arise. Azure Log Analytics can collect log data from all microservices, providing a centralized location for detailed investigation and correlation of multi-tier diagnostics data. ## 4\. Data Analysis for Continuous Improvement After collecting this wealth of information, effective analysis is crucial for driving system improvements. Azure Monitor Logs,powered by Kusto Query Language (KQL), allows for sophisticated analysis. For example, we can create queries to identify patterns, such as peak usage times, most frequent error types, or ride booking trends. Azure’s integration with Power BI is another invaluable tool. By feeding our telemetry data into Power BI, we can generate detailed reports and visualizations, making it easier to understand and present our findings. ## 5\. Conclusion Effectively monitoring a Dockerized distributed microservices architecture orchestrated by Kubernetes in an Azure environment is a complex, but rewarding task. It involves utilizing an array of Azure tools, such as Azure Monitor for containers, Azure Application Insights, Azure Event Hubs, Azure Service Bus and Azure Log Analytics, among others. The global taxi app case study demonstrates how these tools can be integrated to provide comprehensive real-time monitoring and deep data analysis. By focusing on monitoring events, messages and logs, we can maintain a clear overview of our system’s health and performance. This visibility allows us to respond swiftly to any issues, ensuring high availability and an optimal user experience. Furthermore, the effective analysis of telemetry data is crucial for driving continuous improvement in our services. With Azure Monitor Logs and Power BI, we can delve into this data to uncover insights that can guide our system’s evolution. In conclusion, a Dockerized distributed microservices architecture managed by Kubernetes, with its inherent complexities, necessitates an equally sophisticated approach to monitoring and data analysis. Azure, with its robust and versatile toolset, provides an environment where these architectures can thrive. By taking full advantage of these resources, we can ensure our systems are efficient, reliable and continually improving, as evidenced by our global taxi app. --- ## Microservices as APIs URL: https://tgoliveira11.tech/blog/2022-11-22-microservices-functions Published: 2022-11-22 Updated: 2026-07-24 Category: Software & Solution Architecture Tags: api-design, api-management, apis, architecture-decision, asynchronous-processing, azure, azure-api-management, azure-event-grid, azure-functions, azure-logic-apps, cloud-architecture, cloud-computing, cloud-services, cost-efficiency, distributed-systems, event-driven-architecture, faas, function-as-a-service, global-taxi-app, microservices, scalability, serverless-architecture, serverless-functions, service-communication, software-architecture, solution-architecture, synchronous-communication, system-flexibility Excerpt: APIs, Functions and More - Architecting a Global Taxi App with Azure As we journey through the dynamic realm of software development, we frequently encounter pivotal architectural choices. One such crossroad is selecting the optimal method for implementing our microservices: should we utilize APIs or serverless functions? In the wide-ranging ecosystem of cloud providers, Azure stands out with a robust infrastructure that supports both. In this article, we’ll examine the ideal scenarios for each option using the context of a global taxi app and explore other potential alternatives. ## Microservices as APIs APIs (Application Programming Interfaces) are a prevalent method for implementing microservices. They function as contracts, enabling various services to interact without knowledge of the underlying implementation. In Azure, the API Management services offer scalable, secure and robust APIs. Consider a global taxi app. The application’s user interface might need to communicate with a backend service to retrieve real-time location data for nearby taxis. This is a classic use case for an API, where direct communication between services is required and the flow of data is bidirectional. Furthermore, the client (the UI in this case) expects an immediate response. Azure API Management provides a host of features, including rate limiting, caching, analytics and a developer portal. These functions allow for comprehensive control, monitoring and optimization of your APIs. The service also supports a variety of protocols, including REST, SOAP and GraphQL, providing flexibility in service implementation and communication. ## Microservices as Functions On the other side of the coin, we have Functions-as-a-Service (FaaS), a category of serverless computing. Azure Functions is a service that enables developers to write event-driven code that scales automatically in response to demand. In the context of our global taxi app, consider the requirement of sending email receipts to customers after a ride. This operation can be performed asynchronously and doesn’t require an immediate response from the client. An Azure Function would be ideal for such a task. Azure Functions shine in their scalability and cost-effectiveness. With Azure, you pay only for the time your function runs and scaling is handled automatically. This lets developers focus on crafting code that delivers value, rather than managing infrastructure. ## Other Possibilities Azure also offers other service models such as Logic Apps for workflow-based integration and Event Grid for event routing, which can be instrumental in your microservices architecture. For instance, the taxi app might have a workflow that involves checking the availability of drivers, assigning a driver to a customer, calculating the fare at the end of the ride and then processing the payment. This multi-step process can be orchestrated using Azure Logic Apps, which offer a visual designer and a variety of ready-to-use connectors. Azure Event Grid, designed for high-volume, high-speed event routing, can be used for real-time tracking of taxi locations. As taxis move, they can send events to the Event Grid, which can then distribute these events to both the customer-facing and driver-facing services. ## Conclusion Whether to employ APIs or functions in the design of your microservices largely depends on your application’s specific needs. APIs are the go-to choice for synchronous, bidirectional communication, while Azure Functions excel in handling asynchronous, event-driven tasks. However, APIs and functions are not mutually exclusive choices. A well-designed microservices architecture can leverage both APIs and functions, using each where it is most suitable. Azure’s Logic Apps and Event Grid can also play a significant role in creating a robust, flexible and scalable system. Ultimately, the decision is about selecting the right tool for the right job, taking into account the specific demands of your application and the advantages each option provides. Azure, with its broad array of services, offers a versatile platform to make these architectural choices and implement them effectively. --- ## Enhancing Database Scalability with Cloud Solutions and Sharding - A Deeper Dive Using Microsoft Azure URL: https://tgoliveira11.tech/blog/2022-10-10-scalability-sharding Published: 2022-10-10 Updated: 2026-07-24 Category: Software & Solution Architecture Tags: active-geo-replication, azure, azure-cache-for-redis, azure-cosmos-db, azure-sql-database, big-data, caching, cloud-architecture, cloud-computing, cloud-databases, data-management, database-architecture, database-performance, database-scaling, disaster-recovery, distributed-databases, elastic-database, geo-replication, global-applications, high-availability, horizontal-scaling, low-latency, performance-tuning, replication, scalability, sharding, software-architecture, vertical-scaling Excerpt: Empowering Data Growth - Leveraging Microsoft Azure and Sharding for Optimal Database Scalability ## Introduction Throughout my journey as a seasoned software engineer, I have engaged with a myriad of projects, each presenting its unique challenges and opportunities. In today’s big data landscape, one of the most pervasive challenges is effectively scaling databases to accommodate ever-expanding data loads. In this article, I will delve into the essential strategies for scaling databases, with a particular focus on sharding using cloud solutions. Microsoft’s suite of products will serve as prime examples throughout this discourse. ## Understanding Database Scaling: Vertical and Horizontal Scaling a database system essentially means increasing its capacity to handle more data. This enhancement can be achieved through vertical scaling (scaling up) or horizontal scaling (scaling out). The former involves augmenting the capacity of a single database server by adding more potent hardware resources, while the latter adds more servers to distribute the database load. While vertical scaling has its place, it is inherently limited by the maximum capacity of available hardware. Horizontal scaling, on the other hand, affords virtually limitless scalability. The trade-off, however, is additional complexity in ensuring data management and consistency. ## Exploiting Cloud Solutions for Scaling: The Microsoft Azure Edge Cloud-based solutions are an excellent medium for achieving horizontal scaling of databases. By leveraging the cloud, organizations can secure high availability, disaster recovery and virtually limitless scalability without the hefty upfront investment and maintenance costs associated with physical, on-premises servers. Microsoft’s Azure is an exemplary cloud platform that facilitates database scaling. Azure SQL Database, a fully managed relational database service, offers automatic scaling that adapts resources based on workload requirements, ensuring cost-effective scalability since you only pay for the resources utilized. Another potent offering from Microsoft is the Azure Cosmos DB, a globally distributed, multi-model database service that provides frictionless horizontal scaling. It automatically distributes data across various regions, guaranteeing high availability and low latency for your applications, irrespective of the geographic location of users. ## Deeper into Sharding Sharding is a crucial technique for achieving horizontal scaling in cloud databases. Essentially, sharding involves dividing and distributing the data across multiple databases (shards) to enhance performance and enable growth. This technique is particularly beneficial for businesses with user bases spread across various regions, like a global taxi app. Consider a taxi app serving millions of users across the globe. By implementing sharding, you could distribute data based on geographic regions, with each region having its shard. This way, when a user in New York accesses the app to book a taxi, the request is routed to the shard containing data for New York, reducing latency and improving app performance. Moreover, sharding enables the app to handle increasing data loads as the user base grows, as new shards can be added to accommodate users in new regions. Azure SQL Database offers comprehensive support for sharding through the Elastic Database client library. This approach provides data-dependent routing and multi-shard queries, making it easier to manage and query sharded databases. It also manages shard maps, simplifying the process of adding or removing shards as your needs evolve. ## Replication and Caching Replication and caching are also advantageous for scaling databases. Replication entails creating copies of your database to enhance read performance and ensure data availability. Azure SQL Database supports active geo-replication, allowing for the creation of up to four readable secondary databases and auto-failover groups, facilitating automatic failover to a secondary during an outage. Caching involves storing a copy of frequently accessed data in a ‘cache’ to alleviate the load on the database and bolster application performance. Azure offers Azure Cache for Redis, a fully managed caching service, which can significantly accelerate applications by reducing data access latency. ## Monitoring and Performance Tuning As your database scales, monitoring its performance and making necessary adjustments becomes increasingly important. Azure SQL Database provides intelligent features that continuously monitor database usage and dynamically adapt to changing demands. The service also offers recommendations for performance improvements, like indexing strategies and parameter configuration. ## Conclusion In a data-driven world, the ability to scale databases to meet growing data needs is a non-negotiable requirement for businesses. Cloud-based solutions, such as Microsoft Azure, provide powerful, flexible and cost-effective options for database scaling. A deeper understanding of techniques like sharding can further enhance these capabilities, as illustrated with the example of a global taxi app. Sharding is not just a tool for managing large-scale databases; it’s a strategy that can fuel growth, improve user experience and provide businesses with the flexibility they need to navigate a rapidly evolving digital landscape. By strategically leveraging these technologies and approaches, businesses can efficiently manage their data, improve application performance and ultimately, deliver superior value to their customers. --- ## Choosing a Distributed Cache: Consistency, Availability, Cost and Operational Trade-offs URL: https://tgoliveira11.tech/blog/2022-09-16-in-memory-cache Published: 2022-09-16 Updated: 2026-07-24 Category: Software & Solution Architecture Tags: availability, caching, cloud-architecture, consistency, distributed-cache, distributed-systems, performance, solution-architecture, system-design Excerpt: A decision framework for selecting and operating distributed caching based on data semantics, failure behavior, consistency, cost, and organizational capability. *A cache can reduce latency and cost, but it also creates another version of truth. The first decision is not which product to use. It is whether the system should cache the data at all.* Distributed caches are often introduced with a simple argument: > The database is too slow, so place the data in memory. That can work. It can also turn a straightforward data flow into a distributed consistency problem. A cache changes more than latency. It affects: - correctness; - failure behavior; - data ownership; - capacity; - operational complexity; - security; - observability; - cost; - incident response. The decision should therefore begin with the business and data semantics, not a comparison table of Redis, Memcached, Hazelcast, Ignite, Aerospike, Couchbase, or managed cloud offerings. The product matters. The operating model matters more. ## Start With the Problem Before choosing a cache, identify the constraint. Common goals include: - reducing read latency; - protecting a database from repeated reads; - absorbing traffic spikes; - avoiding repeated expensive computation; - storing session state; - coordinating rate limits; - sharing temporary state across instances; - reducing calls to a paid or constrained dependency; - enabling fast lookup of derived data. These are different problems. A session store has different correctness requirements from a product catalog. A rate limiter has different atomicity requirements from a rendered-page cache. An expensive analytical result has different freshness expectations from an account balance. If the problem is unclear, the cache becomes a generic performance layer with undefined correctness. ## Ask Whether Caching Is Appropriate A cache may be unnecessary when: - the database query can be indexed or redesigned; - a materialized view or precomputed data product is more appropriate; - the response is not requested frequently; - the data changes too often; - stale values are unacceptable; - invalidation cannot be defined; - the team cannot operate another distributed dependency; - the cost saved is smaller than the complexity added. Caching is not a substitute for understanding the workload. Measure: - request rate; - latency distribution; - database time; - repeated-query ratio; - object size; - cardinality; - update frequency; - acceptable staleness; - miss penalty; - dependency cost; - peak behavior. The strongest cache is the one the system does not need. ## Define the Source of Truth A cache should not accidentally become an ungoverned database. For every cached item, define: - authoritative source; - owner; - key; - value schema; - freshness requirement; - invalidation trigger; - fallback behavior; - data classification; - retention; - maximum acceptable staleness. The application should know whether the cache contains: - a copy; - a derived value; - temporary state; - coordination state; - the only current value. That last case is no longer ordinary caching. It is primary data storage and needs stronger durability, recovery, and governance. ## Select a Caching Pattern Deliberately ### Cache-Aside The application reads the cache first. On a miss, it reads the source and populates the cache. Advantages: - simple; - only requested data is cached; - resilient to a cache flush when the source remains available. Risks: - stale data; - duplicated loading logic; - many concurrent misses; - first request pays the full latency; - invalidation remains explicit. ### Read-Through The cache layer loads data from the source. Advantages: - application code can be simpler; - loading behavior is centralized. Risks: - tighter coupling to cache capabilities; - less transparent failure behavior; - product-specific implementation. ### Write-Through Writes update the cache and backing store synchronously. Advantages: - cache remains current after successful writes; - predictable read behavior. Risks: - write latency; - dual-system failure handling; - transaction boundaries; - unused data may be cached. ### Write-Behind Writes reach the cache first and are persisted asynchronously. Advantages: - low write latency; - batching opportunity. Risks: - data loss; - ordering; - replay; - complex recovery; - the cache behaves like a primary system. Write-behind is not a performance toggle. It is an architectural commitment. ## Consistency Is a Product Decision Ask what happens when the cache and source disagree. Possibilities include: - stale reads are harmless; - stale reads confuse users; - stale reads violate a contract; - stale reads create financial loss; - stale reads create safety risk. The acceptable model may be: - eventual consistency; - bounded staleness; - read-your-writes; - monotonic reads; - strong consistency for selected operations; - no caching for critical data. A single application may need several models. For example: - public catalog content can tolerate minutes of staleness; - user preferences may need read-your-writes; - permissions should usually be current; - financial balance may require authoritative reads; - configuration may require version-aware invalidation. Do not apply one TTL to all data because it is convenient. ## Invalidation Must Be Designed Cache invalidation is difficult because the system must know when a cached answer is no longer valid. Strategies include: - time-to-live; - explicit deletion after a write; - versioned keys; - event-driven invalidation; - dependency-based invalidation; - namespace rotation; - short caching plus background refresh. TTL is not a complete invalidation strategy. It defines how long an error may survive. For each value, ask: - What event makes it invalid? - Who publishes that event? - What happens if the event is lost? - Can consumers process it more than once? - How are old keys removed? - How will schema changes affect values? - How is invalidation tested? Versioned keys can simplify migrations: ```text customer-profile:v3:{customer-id} ``` They do not remove the need to control old data and memory growth. ## Prevent Cache Stampedes When a popular key expires, many requests may attempt to rebuild it simultaneously. This can overload the source the cache was meant to protect. Controls include: - request coalescing; - distributed locks with careful timeouts; - stale-while-revalidate; - probabilistic early refresh; - jittered TTLs; - background refresh; - bounded concurrency; - circuit breakers. A lock can also create failure if its owner dies or the timeout is wrong. The objective is to limit duplicate work, not to introduce an indefinite dependency. ## Design for Hot Keys and Uneven Workloads Traffic is rarely uniform. A small number of keys may receive a large share of requests. Hot keys can cause: - overloaded partitions; - network concentration; - CPU bottlenecks; - eviction pressure; - latency spikes. Potential mitigations include: - local near-cache; - replication; - key sharding for counters; - precomputation; - batching; - different TTLs; - request coalescing; - workload-specific data structures. Do not assume adding nodes solves a single-key bottleneck. ## Decide What Failure Means The cache will fail. Possible failures include: - complete outage; - partial partition loss; - network timeout; - stale replicas; - failover; - eviction; - memory pressure; - restart; - corrupted or incompatible serialization; - regional outage. For each use case, choose: ### Fail Open Bypass the cache and use the source. Useful when correctness matters and the source can handle the load. Risk: a cache outage becomes a database outage. ### Fail Closed Reject the operation. Useful when cache state is required for security, coordination, or protection. Risk: availability decreases. ### Degrade Return older data, fewer features, or a simplified response. Useful when bounded staleness is acceptable. The behavior should be intentional and tested. ## Persistence Is Not the Same as Durability Some cache products support snapshots, append-only logs, replicas, and disk tiers. That does not automatically make cached data durable enough to become the primary record. Evaluate: - recovery point; - recovery time; - replication acknowledgment; - failover semantics; - backup; - restore testing; - data loss during failover; - consistency after recovery; - regional protection. If losing the cache means losing business records, treat it as a database. ## Multi-Region Changes the Trade-off A cache close to users can reduce latency, but multi-region replication raises questions: - Which region accepts writes? - How are conflicts resolved? - How stale may remote data be? - What happens during a partition? - Can a user move between regions and see older state? - Are keys or values regulated by geography? - Is global replication cost justified? Strong global consistency can add latency and reduce availability. Local caches can improve performance but expose different versions. The right design depends on the data, not a general preference for global distribution. ## Security and Data Classification In-memory does not mean low risk. Cached data may include: - personal data; - tokens; - session state; - permissions; - financial information; - internal configuration; - proprietary results. Controls may include: - network isolation; - TLS; - authentication; - least-privileged access; - encryption; - key and secret management; - tenant separation; - command restrictions; - data minimization; - short retention; - secure backups; - protected logs. Do not place highly sensitive data in a cache merely because access is fast. ## Managed or Self-Hosted? Managed services reduce some operational burden, but not architectural responsibility. A managed cache may provide: - provisioning; - patching; - failover; - monitoring; - backups; - scaling; - support. You still own: - keys and schemas; - TTLs; - invalidation; - consistency; - application failure behavior; - capacity planning; - cost; - security configuration; - incident response. Self-hosting may offer control or portability, but it requires mature operations. Choose based on: - team capability; - reliability target; - data sensitivity; - deployment environment; - required features; - portability; - support; - total cost. License cost is rarely the complete cost. ## Capacity and Cost Estimate: - number of keys; - average key size; - average value size; - metadata overhead; - replication factor; - fragmentation; - headroom; - write amplification; - network traffic; - backup/persistence cost; - cross-region transfer; - peak growth. A rough memory estimate is: ```text (key bytes + value bytes + per-entry overhead) × key count × replication × headroom ``` Real measurements are better than theoretical calculations. Monitor cost per useful hit, not just cluster cost. A cache with a low hit rate may add cost and complexity without reducing source load. ## Observability Track: - hit and miss rate; - latency percentiles; - errors and timeouts; - evictions; - memory usage; - fragmentation; - hot keys; - connection saturation; - command rate; - replication lag; - failovers; - load time; - source fallback; - stampede events; - invalidation delay; - stale-read reports; - cost by workload. A high hit rate is not sufficient. A cache can be fast and wrong. Correlate cache behavior with user and system outcomes. ## A Decision Framework | Question | Lower-complexity choice | Higher-complexity choice | |---|---|---| | Staleness acceptable? | TTL/cache-aside | event invalidation or authoritative read | | Data rebuildable? | no persistence | persistence and recovery | | Load uniform? | basic partitioning | hot-key mitigation | | Cache outage survivable? | fail open | degradation or fail closed | | Single region? | regional service | cross-region design | | Team has operations capacity? | managed service | self-hosted where justified | | Simple values? | key/value | richer data structures or compute | | Cost predictable? | fixed capacity | elastic or tiered service | This table narrows the decision. It does not select a product automatically. ## Product Selection Comes Last After requirements are explicit, compare products across: - data structures; - atomic operations; - consistency; - clustering; - failover; - persistence; - replication; - multi-region support; - security; - observability; - ecosystem; - managed-service availability; - licensing; - operational skill; - cost. A simple managed Redis-compatible service may be the right answer for many workloads. For some, Memcached is sufficient. For others, a distributed data grid, durable key-value store, database, materialized view, or no cache is more appropriate. The right product is the one whose failure and consistency model fit the application. ## Conclusion A distributed cache is not merely a faster place to store data. It is another stateful system with its own truth, failure, security, and cost behavior. The first decision is not which technology has the longest feature list. It is: - what problem is being solved; - which data may be stale; - how invalidation works; - what happens during failure; - whether the organization can operate the result. A cache creates value when it reduces meaningful latency or cost without making correctness and operations unmanageable. That is an architecture decision, not a benchmark result. --- ## Pros and Cons of Remote Working for Software-related Jobs URL: https://tgoliveira11.tech/blog/2022-06-01-remote-work Published: 2022-06-01 Updated: 2026-07-24 Category: Engineering Leadership Tags: collaboration-tools, communication-challenges, cost-savings, digital-workplace, distributed-teams, employee-wellbeing, flexibility, focus-and-discipline, future-of-work, global-teams, hybrid-work, mental-health, productivity, professional-autonomy, remote-collaboration, remote-culture, remote-leadership, remote-productivity, remote-teams, remote-work, software-development, software-industry, talent-access, team-communication, technology-professionals, work-from-home, work-life-balance Excerpt: Advantages and Disadvantages of Remote Working in the Software Industry - A Vision for the Future Remote working has become increasingly popular over the years, particularly in the software industry. As someone who has been working with remote teams since 2008 and working from home since 2015, I can attest to the fact that there are both benefits and drawbacks to this style of work. In this article, I will discuss the advantages and disadvantages of remote working for software-related positions and my visions about future. ## Benefits of Remote Working Flexibility: One of the biggest advantages of remote working is the flexibility it provides. When you work remotely, you can set your own schedule and work from anywhere with an internet connection. This is particularly appealing to those who enjoy traveling or have family obligations. According to the Gartner 2021 Digital Worker Experience Survey, 43% of respondents said that flexibility in working hours helped them achieve greater productivity. Increased productivity: Many people find that they are more productive when working remotely. This is because there are fewer distractions and interruptions than in a traditional office setting. Additionally, remote workers often have more control over their environment, which can help them stay focused and motivated. According to a recent study of more than 30,000 US employees, one day per week spent working from home could boost productivity by **4.8%**(1). Another study found that remote workers can save $4,500 on commuting costs annually and that taking breaks throughout the day can help workers stay productive(2). In a University of Chicago research paper, scientists found that nearly six in ten of their survey respondents reported higher productivity when working remotely, while only 14% proved less productive. On average, remote work productivity was over 7% higher than in-office productivity(3). Cost savings: When you work remotely, you don’t have to worry about commuting or buying lunch every day. This can add up to significant cost savings over time. Remote work can also lead to cost savings for employers. According to a study by Global Workplace Analytics, employers can save an average of $11,000 per year per employee who works remotely half of the time. Access to a larger pool of talent: Remote working allows companies to hire talent from all over the world, rather than being limited to a specific geographic area. This can be particularly beneficial for companies in areas with a shortage of skilled workers. Improved work-life balance: Remote working can help employees achieve a better work-life balance. Without the need to commute or spend time getting ready for work, remote workers have more time to spend with family and friends, pursue hobbies, or take care of other responsibilities. ## Drawbacks of Remote Working Isolation: One of the biggest drawbacks of remote working is the potential for isolation. When you work from home, you don’t have the same opportunities for social interaction as you would in a traditional office setting. This can be particularly challenging for people who are naturally extroverted. Distractions: While remote workers may have fewer interruptions than those in a traditional office, they may also face distractions from their home environment. This can include children, pets, or household chores. Communication challenges: Remote working can create communication challenges, particularly if team members are spread out across different time zones or speak different languages. This can make it more difficult to collaborate and can lead to misunderstandings. Technology issues: When you work remotely, you rely heavily on technology to communicate and collaborate with your team. This can be problematic if there are issues with your internet connection or if you don’t have access to the necessary software or hardware. Lack of structure: Remote working requires a certain degree of self-discipline and structure. Without the structure of a traditional office environment, some remote workers may struggle to stay focused and motivated. Productivity and Creativity: Remote work can also lead to a breakdown of critical bridging connections between team members across the organization which can negatively impact productivity and creativity(4). ## Tips on how to address the drawbacks of Remote Working To minimize feelings of isolation, remote workers can take steps to stay connected with their team and maintain a sense of community. This may include setting up regular virtual meetings or coffee breaks, joining online communities or forums related to their industry, working from a co-working space or coffee shop or scheduling regular check-ins with their manager. To minimize distractions, remote workers can create a dedicated workspace that is free from distractions. This may include setting up a separate office space, using noise-cancelling headphones, or establishing clear boundaries with family members or roommates. To minimize communication challenges, remote workers can make an effort to over-communicate with their team. This may include using video conferencing software for important meetings or discussions, sending regular status updates or progress reports and being proactive in reaching out to team members when needed. To minimize technology issues, remote workers should ensure that they have a reliable internet connection and access to the necessary software and hardware. This may include investing in a high-quality router or modem, using a backup internet connection and having a contingency plan in place in case of technology failures. To minimize a lack of structure, remote workers should establish a consistent schedule and routine. This may include setting regular working hours, taking breaks at consistent intervals and setting clear goals and deadlines for their work. In addition to these strategies, it’s important for remote workers to prioritize their mental and physical health. This may include taking breaks throughout the day to stretch or exercise, practicing mindfulness or meditation. By implementing these strategies and prioritizing their well-being, remote workers can minimize the drawbacks of remote working and enjoy the many benefits that it has to offer. ## The Future of the Software Industry The software industry has seen tremendous growth in recent years and this trend is expected to continue in the future. As more and more businesses move online, there is a growing demand for software developers, engineers, and other technology professionals. Remote working is expected to play a key role in the future of the software industry. With advancements in technology, it has become easier than ever for teams to collaborate and work together remotely. This allows companies to tap into a larger pool of talent and build teams that are truly global. Additionally, remote working has become increasingly popular among younger generations, who prioritize flexibility and work-life balance. As these generations continue to enter the workforce, they are likely to demand remote working options from their employers. ## Conclusion In my opnion, it’s unlikely that the trend of remote working in the software industry will change in the near future. In fact, many experts predict that remote work will continue to grow in popularity, even after the COVID-19 pandemic has subsided. The software industry is uniquely suited for remote work due to the nature of the work itself. Software development can be done from anywhere with an internet connection, making it easy for companies to hire talent from all over the world. Additionally, the industry has been early adopters of many of the technologies that make remote work possible, such as video conferencing, project management tools and collaboration software. Remote work also offers many advantages for both employers and employees. For employers, it can lead to cost savings, increased productivity and access to a larger pool of talent. For employees, it can offer greater flexibility, improved work-life balance, and the ability to work from anywhere. While there are certainly challenges associated with remote work, such as social isolation and difficulty separating work and home life, many companies and employees have found ways to overcome these obstacles. As a result, it seems likely that remote work will continue to be a popular option for the software industry for years to come. Overall, remote working can be a great option for software-related positions, particularly for those who value flexibility and autonomy. However, it’s important to be aware of the potential drawbacks and to take steps to address them. With the right approach, remote working can be a highly rewarding and productive way to work in the software industry. (1) Remote working - does it make us more or less productive?. https://www.weforum.org/agenda/2021/09/remote-working-hybrid-productivity/. (2) Why Remote Workers Are More Productive - Business News Daily. https://www.businessnewsdaily.com/15259-working-from-home-more-productive.html. (3) Workers Are Less Productive Working Remotely (At Least That … - Forbes. https://www.forbes.com/sites/glebtsipursky/2022/11/03/workers-are-less-productive-working-remotely-at-least-thats-what-their-bosses-think/. (4) The Impact Of Remote Work On Productivity And Creativity - Forbes. https://www.forbes.com/sites/forbestechcouncil/2022/01/14/the-impact-of-remote-work-on-productivity-and-creativity/. (5) The remote work experiment that upped productivity 13% - BBC. https://www.bbc.com/worklife/article/20200710-the-remote-work-experiment-that-made-staff-more-productive. --- ## Git Ignore URL: https://tgoliveira11.tech/blog/2022-04-27-git-ignore Published: 2022-04-27 Updated: 2026-07-24 Category: Software & Solution Architecture Tags: code-repository, compiled-files, dev-ops, developer-environment, development-workflow, environment-configuration, git, git-hub-templates, github, gitignore, migration-process, minified-files, repository-management, software-development, software-engineering, source-code-management, source-control, team-collaboration, team-development, tfs-to-git-migration, version-control, visual-studio Excerpt: The correct use of .gitignore file in Visual Studio In one of the projects I work on, I led the migration of the version control system from TFS to Git. One of the points that requires special attention in this process is the correct management of git ignore (.gitignore) file, especially if you work in a team and do not develop alone. That's because Visual Studio works with a series of files that customize the developer's environment by controlling publishing, compilation and a number of other things and in a team environment, we want everyone to be able to work and customize their environment independently. If the management of git ignore is not done well, environment configuration files will end up in version control and then the mess is done. There are also a number of other particularities to look at when managing git ignore, such as files that are generated at compile time; we usually don't want those files in version control, so we should pay attention on how to handle those. I did a search on the internet and found a project on github that helped me a lot in this git ignore management process in this migration and that's why I believe it's worth the record: A collection of .gitignore templates (https://github.com/github/gitignore). I even made a contribution to the project including a template for excluding minified files (https://github.com/tgoliveira11/gitignore/tree/MinifiedFiles), one of the challenges of my migration process. Another thing I learned in pain: how to handle files that shouldn't be in version control and ended up being included due to errors in the management of git ignore. To solve this problem, it is necessary in addition to including the exception in git ignore file, remove from version control the files that should not be there, before including the exception, because while the files are there, the git ignore exception will not work. Here an exemple of the compiled .gitignore file on April 2022: ~~~ ## Ignore Visual Studio temporary files, build results and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore ## User-specific files *.rsuser *.suo *.user *.userosscache *.sln.docstates ## User-specific files (MonoDevelop/Xamarin Studio) *.userprefs ## Mono auto generated files mono_crash.* ## Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ [Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ [Ll]ogs/ ## Visual Studio 2015/2017 cache/options directory .vs/ ## Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ ## Visual Studio 2017 auto generated files Generated\ Files/ ## MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* ## NUnit *.VisualState.xml TestResult.xml nunit-*.xml ## Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c ## Benchmark Results BenchmarkDotNet.Artifacts/ ## .NET Core project.lock.json project.fragment.lock.json artifacts/ ## ASP.NET Scaffolding ScaffoldingReadMe.txt ## StyleCop StyleCopReport.xml ## Files built by Visual Studio *_i.c *_p.c *_h.h *.ilk *.meta *.obj *.iobj *.pch *.pdb *.ipdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *_wpftmp.csproj *.log *.tlog *.vspscc *.vssscc .builds *.pidb *.svclog *.scc ## Chutzpah Test files _Chutzpah* ## Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb ## Visual Studio profiler *.psess *.vsp *.vspx *.sap ## Visual Studio Trace Files *.e2e ## TFS 2012 Local Workspace $tf/ ## Guidance Automation Toolkit *.gpState ## ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user ## TeamCity is a build add-in _TeamCity* ## DotCover is a Code Coverage Tool *.dotCover ## AxoCover is a Code Coverage Tool .axoCover/* !.axoCover/settings.json ## Coverlet is a free, cross platform Code Coverage Tool coverage*.json coverage*.xml coverage*.info ## Visual Studio code coverage results *.coverage *.coveragexml ## NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* ## MightyMoose *.mm.* AutoTest.Net/ ## Web workbench (sass) .sass-cache/ ## Installshield output folder [Ee]xpress/ ## DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html ## Click-Once directory publish/ ## Publish Web Output *.[Pp]ublish.xml *.azurePubxml ## Note: Comment the next line if you want to checkin your web deploy settings, ## but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj ## Microsoft Azure Web App publish settings. Comment the next line if you want to ## checkin your Azure Web App publish settings, but sensitive information contained ## in these scripts will be unencrypted PublishScripts/ ## NuGet Packages *.nupkg ## NuGet Symbol Packages *.snupkg ## The packages folder can be ignored because of Package Restore **/[Pp]ackages/* ## except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ ## Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config ## NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets ## Microsoft Azure Build Output csx/ *.build.csdef ## Microsoft Azure Emulator ecf/ rcf/ ## Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx *.appxbundle *.appxupload ## Visual Studio cache files ## files ending in .cache can be ignored *.[Cc]ache ## but keep track of directories ending in .cache !?*.[Cc]ache/ ## Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs ## Including strong name files can present a security risk ## (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk ## Since there are multiple workflows, uncomment next line to ignore bower_components ## (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ ## RIA/Silverlight projects Generated_Code/ ## Backup & report files from converting an old project file ## to a newer Visual Studio version. Backup files are not needed, ## because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm ServiceFabricBackup/ *.rptproj.bak ## SQL Server files *.mdf *.ldf *.ndf ## Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings *.rptproj.rsuser *- [Bb]ackup.rdl *- [Bb]ackup ([0-9]).rdl *- [Bb]ackup ([0-9][0-9]).rdl ## Microsoft Fakes FakesAssemblies/ ## GhostDoc plugin setting file *.GhostDoc.xml ## Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ ## Visual Studio 6 build log *.plg ## Visual Studio 6 workspace options file *.opt ## Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw ## Visual Studio 6 auto-generated project file (contains which files were open etc.) *.vbp ## Visual Studio 6 workspace and project file (working project files containing files to include in project) *.dsw *.dsp ## Visual Studio 6 technical files *.ncb *.aps ## Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions ## Paket dependency manager .paket/paket.exe paket-files/ ## FAKE - F# Make .fake/ ## CodeRush personal settings .cr/personal ## Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc ## Cake - Uncomment if you are using it ## tools/** ## !tools/packages.config ## Tabs Studio *.tss ## Telerik's JustMock configuration file *.jmconfig ## BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs ## OpenCover UI analysis results OpenCover/ ## Azure Stream Analytics local run output ASALocalRun/ ## MSBuild Binary and Structured Log *.binlog ## NVidia Nsight GPU debugger configuration file *.nvuser ## MFractors (Xamarin productivity tool) working folder .mfractor/ ## Local History for Visual Studio .localhistory/ ## Visual Studio History (VSHistory) files .vshistory/ ## BeatPulse healthcheck temp database healthchecksdb ## Backup folder for Package Reference Convert tool in Visual Studio 2017 MigrationBackup/ ## Ionide (cross platform F# VS Code tools) working folder .ionide/ ## Fody - auto-generated XML schema FodyWeavers.xsd ## VS Code files for those working on multiple tools .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json *.code-workspace ## Local History for Visual Studio Code .history/ ## Windows Installer files from build outputs *.cab *.msi *.msix *.msm *.msp ## JetBrains Rider *.sln.iml ## css min *.min.css ## js min *.min.js --- ## Observability for Agentic Systems: Tracing Decisions, Tools, Data and Failure URL: https://tgoliveira11.tech/blog/2026-07-24-observability-agentic-systems Published: 2026-07-24 Updated: 2026-07-24 Category: AI Engineering Tags: agentic-systems, ai-observability, distributed-tracing, engineering-leadership, enterprise-ai, generative-ai, llmops, reliability Excerpt: A practical approach to observing agentic AI workflows across model decisions, tool calls, data access, policy, retries, latency, cost, and human escalation. *An agent can complete a task through many paths. Production observability must explain which path it took, why, what it accessed, and where the result became unreliable.* A traditional API request is often understandable through a trace: 1. request enters; 2. services are called; 3. data is read or written; 4. a response returns. An agentic workflow is less predictable. The system may: - interpret the user's goal; - retrieve context; - select a tool; - call an API; - inspect the result; - revise a plan; - call another tool; - retry; - ask for clarification; - escalate to a human; - produce a final answer. Two users can ask similar questions and trigger different paths. The same question can produce a different path after a model, prompt, tool description, policy, or data change. This flexibility is useful. It also makes conventional application logging insufficient. Production teams need to answer: - What did the agent believe the user wanted? - Which plan did it form? - Which tools did it consider and select? - Which data did it access? - Which policy decisions applied? - Why did it retry? - Which step consumed latency and cost? - Where did an incorrect answer first appear? - Why did it answer instead of escalating? - Can the result be reproduced? Observability for agentic systems is the discipline of making that path inspectable without exposing sensitive information or drowning operators in raw prompts. ## Begin With a Traceable Unit of Work Every user task needs a stable correlation identity. A useful trace may connect: - session; - conversation; - user turn; - agent run; - model calls; - retrieval; - tool calls; - policy checks; - data queries; - human handoff; - final response. Do not rely on timestamps and manual log searching to reconstruct a workflow. Use trace and span identifiers consistently across: - frontend; - orchestration; - model gateway; - retrieval; - APIs; - data platforms; - policy services; - queues; - human-review systems. The trace should represent one business task, not merely one HTTP request. ## Observe Intent and Planning Carefully The system's internal representation of intent is often where the first error appears. Capture structured fields such as: - detected intent; - entities; - time range; - units; - user role; - required capability; - ambiguity; - risk classification; - selected workflow; - planning version. Avoid logging unrestricted private reasoning or sensitive chain-of-thought-like content. Production observability does not require exposing hidden reasoning. It requires recording the decisions and inputs necessary to explain behavior. For example: ```json { "intent": "retrieve_adjusted_inventory", "facility": "terminal-north", "time_window": "next-operating-window", "unit": "m3", "ambiguity_detected": true, "clarification_requested": true } ``` This is more useful and safer than an unstructured block of model text. ## Trace Tool Selection and Execution For each tool decision, record: - tools available to the agent; - tool selected; - tool version; - reason code or structured selection category; - input schema version; - validated parameters; - authorization context; - execution start and end; - status; - retry; - result size; - result classification; - error category. Do not log credentials or unrestricted sensitive payloads. A tool call should be attributable to: - initiating user; - agent; - delegated identity; - policy decision; - task. This is especially important when an agent can take actions rather than only retrieve information. ## Observe Data Provenance A correct answer depends on correct data. Capture: - source system; - dataset or API; - schema version; - record or result identifiers where permitted; - data timestamp; - freshness; - query hash or safe representation; - row count; - transformation; - unit conversion; - quality status; - authoritative-source decision. For a conversational answer, provenance should survive through the final response. If the response says a value is current, the trace should show the source timestamp and freshness policy that justified that claim. If data from multiple systems conflicts, the trace should show which source was selected and why. ## Treat Policy Decisions as First-Class Spans Authorization and safety controls are often logged separately from application traces. That creates gaps during investigation. A policy span should record: - policy name and version; - subject; - delegated actor; - tenant; - resource; - action; - relevant attributes; - decision; - reason code; - obligations or filters; - latency. Do not log sensitive policy data unnecessarily. The important outcome is that a team can explain why a tool or field was allowed, denied, or redacted. Policy should not be hidden inside the model prompt. It should be observable as an independent decision. ## Make Retries Visible Agents may retry because: - a tool timed out; - a query failed; - validation rejected parameters; - a result was empty; - the model changed its plan; - a policy denied access; - output failed formatting; - confidence was low. Retries can improve reliability. They can also create: - duplicate actions; - unexpected cost; - long latency; - loops; - repeated load; - inconsistent state. Record: - retry reason; - attempt number; - changed input; - backoff; - idempotency key; - outcome; - retry budget. Set explicit limits. An agent that keeps “trying something else” without a bounded strategy is not resilient. It is uncontrolled. ## Separate Model, Tool, and System Failures A final incorrect answer may originate from different layers. ### Model Failure - wrong intent; - inappropriate tool; - unsupported conclusion; - ignored evidence; - poor summary. ### Retrieval or Data Failure - irrelevant context; - stale data; - missing record; - wrong source; - schema change; - incorrect unit. ### Tool Failure - timeout; - malformed response; - partial success; - API contract change; - rate limit. ### Policy Failure - incorrect allow; - incorrect deny; - missing tenant filter; - stale entitlement; - enforcement gap. ### Orchestration Failure - wrong sequence; - lost state; - duplicate action; - retry loop; - context overflow. ### Presentation Failure - changed number; - wrong formatting; - hidden qualifier; - misleading confidence. Classify failures at the layer where they begin. Otherwise, every incident becomes “the AI was wrong,” which is too vague to fix. ## Measure Latency as a Critical Path End-to-end latency is the sum of many steps, but not every step is equally important. Trace: - queue time; - model time; - retrieval time; - policy time; - tool time; - database time; - retries; - human wait; - response rendering. Identify: - critical path; - parallel work; - repeated context construction; - expensive tools; - unnecessary model calls; - serial policy checks; - slow fallbacks. Track percentiles by workflow and complexity, not only a global average. An operational lookup and a complex investigation should have different expectations. ## Attribute Cost to the Task Agent cost can include: - input and output tokens; - embedding and retrieval; - model calls; - tool/API fees; - database compute; - data transfer; - retries; - human review; - incident handling. Record cost at: - model call; - tool call; - agent run; - user task; - feature; - tenant or team where appropriate. Useful metrics include: - cost per successful task; - cost per accepted answer; - cost by workflow; - cost of retries; - cost of failed runs; - cost by model; - cost by complexity. A lower model price does not guarantee a lower task cost if it causes more retries or failures. ## Monitor Quality Signals Without Treating Them as Truth Production signals may include: - thumbs up/down; - user edits; - repeated questions; - answer copied; - task abandoned; - correction requested; - tool result opened; - human escalation; - support ticket; - incident. These are useful, but ambiguous. A copied answer may be wrong. A repeated question may reflect a changed intent. A negative rating may concern tone rather than correctness. Combine behavior with evaluation and trace context. ## Design Privacy Into Observability Agent traces can contain: - user prompts; - personal data; - business data; - retrieved documents; - tool parameters; - API responses; - model output; - security decisions. Logging everything by default creates a second sensitive data platform. Apply: - data minimization; - field classification; - redaction; - tokenization; - sampling; - role-based access; - retention; - encryption; - regional controls; - audit of trace access. Store structured metadata whenever it can answer the operational question without storing full content. Allow deeper capture for controlled debugging, with explicit authorization and limited retention. ## Build Operational Views for Different Audiences ### Engineering Needs: - traces; - errors; - retries; - latency; - cost; - versions; - dependencies. ### Product Needs: - task success; - adoption; - clarification; - abandonment; - workflow performance; - user outcomes. ### Security and Risk Needs: - access decisions; - tool use; - sensitive-data events; - policy violations; - delegated actions; - auditability. ### Operations Needs: - service health; - failed workflows; - degraded dependencies; - escalation queues; - impact. ### Leadership Needs: - reliability; - value; - cost; - risk; - trend; - improvement. One dashboard should not attempt to serve every audience. ## Useful Metrics Track at least: - successful task completion; - clarification rate; - escalation rate; - tool success; - validation rejection; - policy denial; - retry count; - loop prevention; - unsupported-answer rate; - grounding coverage; - end-to-end latency; - cost per successful task; - stale-data detection; - human correction; - incident frequency; - performance by workflow and version. Avoid optimizing one metric in isolation. Reducing escalation may increase unsafe answers. Reducing latency may remove validation. Reducing cost may increase retries. The metric set must preserve the product's risk balance. ## Version Everything Needed for Reproduction A trace should reference: - model and configuration; - prompt template; - tool definitions; - orchestration version; - semantic-layer version; - policy version; - schema version; - retrieval index; - feature flags; - relevant data timestamp. Exact reproduction may still be impossible because models and dependencies can be nondeterministic. The system should at least make the conditions comparable. Without versioning, teams cannot distinguish a random variation from a release regression. ## Human Escalation Must Be Observable When the agent hands work to a person, record: - reason; - risk level; - evidence gathered; - actions attempted; - blocked action; - missing information; - recommended next step; - queue; - response time; - outcome. The human should not need to reconstruct the entire conversation manually. A good handoff transfers context without transferring uncontrolled private data. Escalation is part of the workflow, not an exception outside the system. ## An Incident Investigation Example Suppose an operator reports that an agent returned the wrong adjusted inventory. A useful trace should allow the team to determine: 1. The intent was interpreted as net standard volume. 2. The correct facility and time window were selected. 3. Authorization allowed that facility. 4. The SQL query used the approved view. 5. The source returned a value in liters. 6. A formatting component labeled the value as cubic meters without conversion. 7. The final response preserved the wrong label. 8. The evaluation suite lacked that unit case. This is actionable. “The model hallucinated” is not. ## A Practical Trace Model A task trace can contain spans such as: ```text User task ├── Intent interpretation ├── Risk classification ├── Authorization ├── Semantic lookup ├── Tool selection ├── Query validation ├── Data query ├── Unit and format validation ├── Response generation ├── Output verification └── User feedback or escalation ``` Each span should carry structured status, version, latency, cost, and safe diagnostic fields. This makes the workflow understandable as a system. ## Conclusion Agentic systems are flexible because they can choose paths dynamically. That same flexibility makes them difficult to operate without intentional observability. A production trace must connect: - user intent; - model and orchestration decisions; - tools; - data; - policy; - retries; - latency; - cost; - final output; - human outcome. The goal is not to record every internal token. It is to make consequential decisions and transitions explainable. When an agent succeeds, the team should understand why. When it fails, the team should know where. That is the foundation for improving reliability without treating every problem as mysterious model behavior. --- ## Evaluating Enterprise AI Systems: From Model Accuracy to Operational Trust URL: https://tgoliveira11.tech/blog/2026-07-24-evaluating-enterprise-ai-operational-trust Published: 2026-07-24 Updated: 2026-07-24 Category: AI Engineering Tags: ai-evaluation, ai-quality, engineering-leadership, enterprise-ai, generative-ai, llmops, observability, responsible-ai Excerpt: A practical framework for evaluating enterprise AI across task quality, grounding, policy, reliability, cost, user outcomes, and failure behavior. *A model can produce impressive answers and still fail as a product. Enterprise evaluation must measure the entire system, including data, tools, policy, reliability, cost, and human outcomes.* AI evaluation often begins with a model comparison. A team collects prompts, runs several models, scores the answers, and selects the strongest result. That is useful during exploration. It is not enough for production. An enterprise AI product is rarely just a model. It may include: - retrieval; - enterprise data; - prompts; - tool selection; - APIs; - business rules; - authorization; - formatting; - workflow state; - human review; - user interface; - monitoring. A correct model response can be undermined by stale data, the wrong tool, an authorization error, a conversion problem, or a workflow that hides uncertainty. The real evaluation target is the complete path from user intent to business outcome. ## Start With the Decision the System Supports Evaluation should begin with the purpose of the product. Ask: - Who uses it? - Which decision or task does it support? - What does a useful outcome look like? - What happens when it is wrong? - Which errors are tolerable? - Which errors require blocking or escalation? - How current must the information be? - Which policy constraints apply? A writing assistant and an operational decision-support system should not share the same quality threshold. For a low-risk brainstorming task, creativity may be valuable. For a system reporting inventory, financial exposure, or operational conditions, grounding, precision, authorization, and freshness may dominate. “Good answer” is not a universal category. It is defined by the task and its consequences. ## Evaluate Multiple Layers A practical evaluation framework should separate failure domains. ### 1. Intent Understanding Did the system understand the user's request? Measure: - entity extraction; - time interpretation; - unit interpretation; - domain terminology; - follow-up context; - ambiguity detection; - clarification behavior. A system that generates a polished answer to the wrong interpretation has failed before retrieval or reasoning begins. ### 2. Grounding and Source Selection Did the system use the correct evidence? Measure: - authoritative-source selection; - citation or provenance coverage; - freshness; - retrieval precision; - retrieval recall; - unsupported claims; - conflict handling; - use of prohibited external sources. For enterprise use, the question is not merely whether information is relevant. It is whether the source is approved for that decision. ### 3. Task or Reasoning Quality Did the system perform the intended task correctly? Depending on the product, this may include: - calculation; - classification; - summarization; - query generation; - tool selection; - workflow planning; - extraction; - comparison; - recommendation. A single aggregate score can hide important failure patterns. Score critical dimensions separately. ### 4. Policy and Authorization Did the workflow respect access and business policy? Test: - unauthorized questions; - cross-tenant requests; - indirect disclosure; - restricted fields; - policy conflicts; - delegated access; - high-risk actions; - refusal behavior. Policy compliance should be evaluated deterministically where possible, not only through another model's opinion. ### 5. Output Fidelity Did the final response preserve the data correctly? Evaluate: - units; - precision; - locale; - dates; - time zones; - labels; - totals; - percentages; - uncertainty; - qualifiers; - formatting. This layer matters because a correct retrieval can still become a wrong final answer. ### 6. Reliability and Failure Behavior How does the system behave when dependencies fail? Test: - model timeout; - retrieval outage; - tool error; - partial data; - stale data; - schema change; - empty result; - malformed result; - policy-service outage; - retry exhaustion; - context overflow. A production system needs good failure behavior, not only good happy-path answers. ### 7. Performance and Cost Measure: - end-to-end latency; - time to first useful response; - model calls per task; - tool calls; - retries; - token usage; - data-platform cost; - cost per successful outcome; - cost by task complexity. A quality improvement that multiplies latency or cost may not be viable. ### 8. User and Business Outcome Did the product improve the work? Possible measures include: - task completion; - time saved; - correction rate; - acceptance rate; - escalation; - user confidence calibrated to correctness; - reduced support effort; - improved decision speed; - avoided error; - adoption and retention. A technically strong benchmark result is not proof of business value. ## Build an Evaluation Dataset That Represents Production A random collection of convenient prompts creates a false sense of coverage. An enterprise dataset should include: - common tasks; - high-value tasks; - high-risk tasks; - ambiguous wording; - domain synonyms; - incomplete requests; - long conversations; - adversarial prompts; - stale or conflicting data; - unauthorized requests; - rare edge cases; - historical incidents; - known model weaknesses. Organize examples by: - business domain; - user role; - risk level; - complexity; - data source; - expected tool; - expected refusal or escalation; - language and locale. Do not allow the evaluation set to become static. Production feedback, incidents, schema changes, and new features should add new cases. ## Use Different Evaluators for Different Questions No single evaluator is sufficient. ### Deterministic Checks Best for: - exact calculations; - units; - schema; - authorization; - required fields; - allowed sources; - formatting; - tool-call arguments; - SQL safety; - latency; - cost. ### Human Review Best for: - usefulness; - nuance; - domain correctness; - ambiguity; - tone; - workflow fit; - consequences of subtle errors. Human reviewers need clear rubrics and calibration. Otherwise, disagreement becomes noise. ### Model-Based Grading Useful for: - scalable semantic comparison; - summarization quality; - rubric-based review; - initial triage; - identifying likely regressions. Model graders should be tested against human decisions and should not be used as unquestioned truth. ### Production Signals Useful for: - acceptance; - edits; - retries; - abandonment; - escalation; - support tickets; - incidents; - task completion. A user accepting an answer does not prove it was correct. A user editing it does not prove the system failed. Interpret behavior in context. ## Define Risk-Weighted Metrics A simple average treats all mistakes equally. Production systems should weight failures by consequence. For example: - a minor formatting difference; - an unsupported explanatory sentence; - a wrong unit; - an unauthorized disclosure; - an incorrect operational recommendation. These should not contribute equally to one score. Create severity levels such as: - informational; - minor; - material; - critical. Track both frequency and severity. A release that improves average quality while introducing one critical authorization regression should not pass. ## Test the Complete Workflow Component evaluation remains useful. Test retrieval separately. Test query generation separately. Test policy separately. Then test end to end. A component can perform well in isolation and fail after integration because: - retrieved context is too long; - tool descriptions are ambiguous; - policy removes required fields; - retries change state; - formatting alters units; - the final model ignores tool output; - latency causes timeouts; - one step loses provenance. End-to-end evaluation catches the interactions that component tests miss. ## Evaluate Change, Not Just Absolute Quality Every change should answer: - What improved? - What regressed? - For which user group? - On which task category? - At what cost and latency? - Did critical failures change? - Is the difference statistically and operationally meaningful? Compare: - model versions; - prompt versions; - retrieval changes; - tool descriptions; - semantic-layer changes; - policy changes; - data changes; - formatting logic. Store configuration and dataset versions with results. Without reproducibility, evaluation becomes anecdotal. ## Create Release Gates A production release should have explicit gates. Examples: - no critical authorization failures; - no incorrect unit conversions in critical tasks; - minimum grounding coverage; - maximum unsupported-claim rate; - minimum task success by priority domain; - maximum latency percentile; - maximum cost per successful task; - no material regression against the current release; - required human sign-off for high-risk workflows. Gates should reflect product risk, not arbitrary industry numbers. ## Monitor After Release Preproduction evaluation cannot represent every real interaction. Production monitoring should detect: - new intents; - repeated clarification; - low-confidence tasks; - policy denials; - tool failures; - unsupported answers; - corrections; - latency and cost drift; - changes by user role or domain; - anomalies after data or model changes. Route important failures back into the evaluation dataset. This creates a loop: 1. observe production; 2. classify failures; 3. add test cases; 4. improve the system; 5. evaluate; 6. release; 7. observe again. Evaluation is not a final test phase. It is part of operating the product. ## The Role of Human Escalation A trustworthy system knows when not to answer. Evaluation must test whether escalation happens: - when evidence is missing; - when sources conflict; - when a request is ambiguous; - when policy blocks access; - when the action is irreversible; - when confidence is insufficient; - when the result is outside normal operating ranges. Escalation quality also matters. A useful response explains: - what is missing; - why it matters; - what can still be stated safely; - which next step is appropriate. Refusal without context frustrates users. Confidence without evidence creates risk. ## A Practical Scorecard | Dimension | Example question | |---|---| | Intent | Did the system interpret the operator's meaning? | | Grounding | Did it use approved, current sources? | | Task quality | Was the calculation, query, or summary correct? | | Policy | Was access permitted and correctly enforced? | | Fidelity | Were units, dates, and precision preserved? | | Reliability | Did dependency failures produce safe behavior? | | Performance | Was the result delivered within the target? | | Cost | Was the resource use justified by the task? | | Outcome | Could the user complete the work safely? | | Escalation | Did the system stop and explain when it should? | The scorecard should lead to decisions, not a decorative dashboard. ## Conclusion Evaluating an enterprise AI system is not the same as ranking models. The product succeeds only when the complete system: - understands the request; - uses the right evidence; - performs the task correctly; - respects policy; - preserves the data; - fails safely; - operates within latency and cost constraints; - improves the user's work. Model accuracy is one component. Operational trust is the outcome. That is the standard production evaluation should measure.