<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Walter Emmanuel Salas Jimenez</title>
    <description>The latest articles on Forem by Walter Emmanuel Salas Jimenez (@wsalas651).</description>
    <link>https://forem.com/wsalas651</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3064587%2F23e99d54-b07a-4ebd-bd2b-aea91eea29e7.jpg</url>
      <title>Forem: Walter Emmanuel Salas Jimenez</title>
      <link>https://forem.com/wsalas651</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/wsalas651"/>
    <language>en</language>
    <item>
      <title>🔭 Observability Practices: The 3 Pillars with a Node.js + OpenTelemetry Example</title>
      <dc:creator>Walter Emmanuel Salas Jimenez</dc:creator>
      <pubDate>Mon, 01 Dec 2025 19:09:09 +0000</pubDate>
      <link>https://forem.com/wsalas651/observability-practices-the-3-pillars-with-a-nodejs-opentelemetry-example-11k7</link>
      <guid>https://forem.com/wsalas651/observability-practices-the-3-pillars-with-a-nodejs-opentelemetry-example-11k7</guid>
      <description>&lt;h2&gt;
  
  
  🚀 Demystifying Observability: A Practical Guide with Node.js, OpenTelemetry, Prometheus, and Grafana
&lt;/h2&gt;

&lt;p&gt;🧐 &lt;strong&gt;Link to the practice repository:&lt;/strong&gt; &lt;a href="https://github.com/Wsalas651/observability-demo" rel="noopener noreferrer"&gt;Wsalas651/observability-demo&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;In the modern era of &lt;strong&gt;distributed systems&lt;/strong&gt; and microservices, relying solely on traditional &lt;strong&gt;monitoring&lt;/strong&gt; is like trying to diagnose a complex illness with just a thermometer. Monitoring tells you &lt;em&gt;when&lt;/em&gt; a problem exists (e.g., "CPU usage is high"), but &lt;strong&gt;Observability&lt;/strong&gt; gives you the tools to understand &lt;em&gt;why&lt;/em&gt; the problem is happening.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Observability?
&lt;/h3&gt;

&lt;p&gt;Observability is a property of a system that allows an operator to infer its internal state by examining its external outputs. It's the ability to ask arbitrary questions about your system without having to release new code to answer them.&lt;/p&gt;

&lt;p&gt;It is universally defined by its &lt;strong&gt;three pillars&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Metrics (The Numeric Story):&lt;/strong&gt; These are aggregated numerical measurements collected over time. They tell you &lt;em&gt;how much&lt;/em&gt; or &lt;em&gt;how often&lt;/em&gt; something is happening. They are best for time-series analysis and spotting trends.

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Examples:&lt;/em&gt; Request count, latency percentiles, CPU usage, memory consumption.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Logs (The Discrete Events):&lt;/strong&gt; These are immutable, timestamped text records of discrete events that occurred at a specific point in time. They are the "what happened when" data.

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Examples:&lt;/em&gt; User X logged in, a database query failed, a transaction was processed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Traces (The Request's Journey):&lt;/strong&gt; A trace represents the end-to-end path of a single request or transaction as it flows through a distributed system. They are crucial for debugging latency issues in microservice architectures.

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Examples:&lt;/em&gt; A user clicking a button initiates calls across Service A, Service B, and a Database, showing the time spent in each.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why Observability Matters
&lt;/h3&gt;

&lt;p&gt;In a monolithic application, debugging might be challenging, but the data is centralized. In a microservices environment, a single user click can trigger a chain of calls across dozens of services written in different languages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observability allows you to:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reduce Mean Time To Resolution (MTTR):&lt;/strong&gt; Spend less time guessing and more time fixing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handle Unknown-Unknowns:&lt;/strong&gt; Debug issues you never anticipated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improve System Health:&lt;/strong&gt; Use the data to refactor bottlenecks and optimize resource usage.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🛠️ Practical Example: Observability with Node.js and OpenTelemetry
&lt;/h2&gt;

&lt;p&gt;We will demonstrate how to instrument a simple &lt;strong&gt;Node.js Express API&lt;/strong&gt; using &lt;strong&gt;OpenTelemetry&lt;/strong&gt; for both metrics and traces, and then visualize that data using &lt;strong&gt;Prometheus&lt;/strong&gt; and &lt;strong&gt;Grafana&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Observability Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Application:&lt;/strong&gt; A simple Node.js Express API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instrumentation:&lt;/strong&gt; &lt;strong&gt;OpenTelemetry (OTEL)&lt;/strong&gt;. This is a vendor-neutral standard for instrumenting code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metrics Store:&lt;/strong&gt; &lt;strong&gt;Prometheus&lt;/strong&gt;, which scrapes metrics from the app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visualization:&lt;/strong&gt; &lt;strong&gt;Grafana&lt;/strong&gt;, for building dashboards on the Prometheus data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Orchestration:&lt;/strong&gt; &lt;strong&gt;Docker Compose&lt;/strong&gt; to run the full stack.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 1: Instrumenting the Node.js API
&lt;/h3&gt;

&lt;p&gt;Our application needs to expose its internal state. We use &lt;strong&gt;OpenTelemetry&lt;/strong&gt; to set up &lt;strong&gt;tracing&lt;/strong&gt; and custom &lt;strong&gt;Prometheus&lt;/strong&gt; metrics.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. OpenTelemetry Tracing Setup (&lt;code&gt;app/src/tracer.js&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;OpenTelemetry handles the heavy lifting of exporting trace data. This file initializes the tracer, ensuring every request across services is connected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/src/tracer.js&lt;/span&gt;
&lt;span class="c1"&gt;// tracer.js - sets up OpenTelemetry to send traces to Jaeger&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NodeSDK&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@opentelemetry/sdk-node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getNodeAutoInstrumentations&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@opentelemetry/auto-instrumentations-node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;JaegerExporter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@opentelemetry/exporter-jaeger&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setupTracing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;serviceName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;observability-demo-app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;exporter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JaegerExporter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://jaeger:14268/api/traces&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="c1"&gt;// For UDP agent: { host: 'jaeger', port: 6832 }&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sdk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NodeSDK&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;traceExporter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;exporter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;instrumentations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;getNodeAutoInstrumentations&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
    &lt;span class="nx"&gt;serviceName&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OpenTelemetry initialized&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error starting OpenTelemetry SDK&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Optionally handle graceful shutdown&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SIGTERM&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Tracing terminated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error terminating tracing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;setupTracing&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Custom Metrics (app/src/metrics.js)
&lt;/h4&gt;

&lt;p&gt;While OTEL provides basic metrics, custom metrics are essential for business logic. We use the standard prom-client library (which OTEL often leverages) to define and track two core metrics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// metrics.js - registers Prometheus metrics&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prom-client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Default metrics (CPU, memory etc.)&lt;/span&gt;
&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collectDefaultMetrics&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Create a Registry (could use default registry)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;register&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// HTTP request metrics&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;httpRequestCounter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http_requests_total&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;help&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Total number of HTTP requests&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;labelNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;method&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;route&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;httpRequestDuration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Histogram&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http_request_duration_seconds&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;help&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Duration of HTTP requests in seconds&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;labelNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;method&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;route&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;buckets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.005&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;metricsMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;httpRequestDuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startTimer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;finish&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;httpRequestCounter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inc&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;metricsMiddleware&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Orchestration with Docker Compose
&lt;/h3&gt;

&lt;p&gt;Your docker-compose.yml ties the entire stack together, defining the application, Prometheus, and Grafana services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3.8'
services:
  app:
    build: ./app
    image: observability-demo-app:latest
    container_name: observability-demo-app
    ports:
      - "3000:3000"
    environment:
      - PORT=3000
    depends_on:
      - prometheus
      - jaeger

  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
    command:
      - "--config.file=/etc/prometheus/prometheus.yml"
    ports:
      - "9090:9090"

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    depends_on:
      - prometheus
    ports:
      - "3001:3000"
    volumes:
      - ./grafana/provisioning:/etc/grafana/provisioning:ro
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin

  jaeger:
    image: jaegertracing/all-in-one:1.39
    container_name: jaeger
    ports:
      - "16686:16686"   # Jaeger UI
      - "14268:14268"   # Jaeger collector (HTTP)

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Seeing the README.md of the repository.
&lt;/h3&gt;

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

&lt;p&gt;Observability is a critical capability, not just a toolset. By adopting a standard like OpenTelemetry and leveraging powerful open-source tools like Prometheus and Grafana, you gain the ability to move beyond basic monitoring and truly understand the internal behavior of your distributed applications.&lt;/p&gt;

&lt;p&gt;Stop guessing, start observing! Happy coding! 🚀&lt;/p&gt;

</description>
      <category>node</category>
      <category>observability</category>
      <category>devops</category>
      <category>grafana</category>
    </item>
    <item>
      <title>🚀 Build a Remote MCP Server That Connects to Any MCP Client (Claude, VSCode &amp; More)</title>
      <dc:creator>Walter Emmanuel Salas Jimenez</dc:creator>
      <pubDate>Mon, 01 Dec 2025 16:18:51 +0000</pubDate>
      <link>https://forem.com/wsalas651/build-a-remote-mcp-server-that-connects-to-any-mcp-client-claude-vscode-more-3i9d</link>
      <guid>https://forem.com/wsalas651/build-a-remote-mcp-server-that-connects-to-any-mcp-client-claude-vscode-more-3i9d</guid>
      <description>&lt;p&gt;The &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt; is quickly becoming the new universal standard for connecting AI assistants with real-world tools. Imagine giving Claude, VSCode, or any MCP-compatible client the power to interact with your APIs, databases, scripts, or cloud resources—securely and consistently.&lt;/p&gt;

&lt;p&gt;In this guide, you’ll learn:&lt;/p&gt;

&lt;p&gt;✨ What an MCP Server is&lt;br&gt;&lt;br&gt;
⚙️ How to build one&lt;br&gt;&lt;br&gt;
🔌 How MCP Clients connect&lt;br&gt;&lt;br&gt;
🧰 Deployment options (Cloudflare, Cloud Run, custom hosting)&lt;br&gt;&lt;br&gt;
💻 Example code + a public repository template  &lt;/p&gt;


&lt;h2&gt;
  
  
  🧠 What Is an MCP Server?
&lt;/h2&gt;

&lt;p&gt;An &lt;strong&gt;MCP Server&lt;/strong&gt; is a backend service that exposes tools, resources, prompts, or custom logic to AI clients through a standardized protocol.&lt;/p&gt;

&lt;p&gt;Think of it as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🛠️ &lt;strong&gt;Your own plugin system for AI models.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
If you can code it, an LLM can use it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With an MCP Server, you can expose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📡 API requests
&lt;/li&gt;
&lt;li&gt;🗂️ Database queries
&lt;/li&gt;
&lt;li&gt;💾 File operations
&lt;/li&gt;
&lt;li&gt;🔐 Secure internal tools
&lt;/li&gt;
&lt;li&gt;🧮 Business logic
&lt;/li&gt;
&lt;li&gt;🛠️ Any custom code/functionality
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And any &lt;strong&gt;MCP Client&lt;/strong&gt; can use it immediately:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Desktop
&lt;/li&gt;
&lt;li&gt;Claude on Web (with model contexts)
&lt;/li&gt;
&lt;li&gt;VSCode MCP Extension
&lt;/li&gt;
&lt;li&gt;Cursor (partial)
&lt;/li&gt;
&lt;li&gt;Custom terminals or agents
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🔌 How MCP Servers Connect to Clients
&lt;/h2&gt;

&lt;p&gt;MCP uses a clean JSON-based message protocol on top of common transports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔗 &lt;strong&gt;WebSockets&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🧵 &lt;strong&gt;STDIO&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🌐 &lt;strong&gt;HTTP Streaming&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;☁️ &lt;strong&gt;Cloudflare Workers Channels&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🚀 &lt;strong&gt;Google Cloud Run HTTP endpoints&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Clients send messages like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;initialize&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;list_tools&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;call_tool&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;read_resource&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the server responds with structured results.&lt;/p&gt;

&lt;p&gt;This design makes MCP servers &lt;strong&gt;portable, cloud-ready, and language-agnostic&lt;/strong&gt; 💡.&lt;/p&gt;


&lt;h2&gt;
  
  
  🛠️ Minimal MCP Server Example (TypeScript)
&lt;/h2&gt;

&lt;p&gt;Below is a simple starter server that exposes one tool called &lt;code&gt;hello_world&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Tool&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@modelcontextprotocol/sdk/server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;example-mcp-server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello_world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Returns a greeting message.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Hello, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!`&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple server:&lt;/p&gt;

&lt;p&gt;✔️ Registers one tool&lt;br&gt;
✔️ Accepts a name argument&lt;br&gt;
✔️ Returns a message response&lt;br&gt;
✔️ Works with any MCP client&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 Connecting Your MCP Server to Claude Desktop
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"servers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My MCP Server"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"websocket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wss://your-public-server.com"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧩 Connecting to VSCode (MCP Extension)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"myServer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"./dist/server.js"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Your tools will show up in the MCP panel inside VSCode 📎.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  ☁️ Deployment Options
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🌩️ &lt;strong&gt;1. Cloudflare Workers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developers.cloudflare.com/agents/guides/remote-mcp-server/" rel="noopener noreferrer"&gt;Perfect for serverless, instant deployments:&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clone example project&lt;/li&gt;
&lt;li&gt;Configure wrangler.toml&lt;/li&gt;
&lt;li&gt;Run wrangler deploy&lt;/li&gt;
&lt;li&gt;Fast, free-tier friendly, globally distributed.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  🚀 &lt;strong&gt;2. Google Cloud Run&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://cloud.google.com/blog/topics/developers-practitioners/build-and-deploy-a-remote-mcp-server-to-google-cloud-run-in-under-10-minutes" rel="noopener noreferrer"&gt;Google published a full tutorial + sample repo:&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cloud Run gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auto-scaling&lt;/li&gt;
&lt;li&gt;Pay-per-request&lt;/li&gt;
&lt;li&gt;HTTPS by default&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🧱 &lt;strong&gt;3. Custom Hosting&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Run it anywhere:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2&lt;/li&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Kubernetes&lt;/li&gt;
&lt;li&gt;PM2 on a VPS&lt;/li&gt;
&lt;li&gt;Local dev machine&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  MCP only requires a transport channel (WebSocket or STDIO).
&lt;/h2&gt;

&lt;h2&gt;
  
  
  🎯 Why Build Your Own MCP Server?
&lt;/h2&gt;

&lt;p&gt;💼 Enterprise automation&lt;br&gt;
🛡️ Secure tool access&lt;br&gt;
🔌 Standardized integrations&lt;br&gt;
🧩 One tool layer usable across all AI apps&lt;br&gt;
⚡ Faster development for agentic workflows&lt;br&gt;
🌍 Cloud portability and vendor independence&lt;/p&gt;

&lt;h2&gt;
  
  
  If you're building AI-powered tools, MCP Servers are the future.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  📦 Example Public Repository
&lt;/h2&gt;

&lt;p&gt;Here is a clean, minimal, ready-to-use MCP Server starter repo:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/LerianStudio/lerian-mcp-server" rel="noopener noreferrer"&gt;GitHub Example (Community Template):&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This repository includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TypeScript source code&lt;/li&gt;
&lt;li&gt;Example tools&lt;/li&gt;
&lt;li&gt;Transport implementation&lt;/li&gt;
&lt;li&gt;Scripts for running the server&lt;/li&gt;
&lt;li&gt;Instructions for connecting to Claude and VSCode&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  You can fork it and start adding your own tools immediately.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  🎉 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;MCP is transforming how AI apps interact with the real world.&lt;br&gt;
By building your own MCP Server, you unlock:&lt;/p&gt;

&lt;p&gt;✨ Secure, structured, AI-driven automation&lt;br&gt;
✨ Native integration with Claude &amp;amp; IDEs&lt;br&gt;
✨ Cloud-ready extensibility&lt;br&gt;
✨ A reusable tool layer for any AI agent&lt;/p&gt;

&lt;p&gt;If you want, I can also:&lt;/p&gt;

&lt;p&gt;🔧 Build a full boilerplate MCP server repo for you&lt;br&gt;
📘 Write a step-by-step Cloudflare or Cloud Run deployment guide&lt;br&gt;
🧩 Generate architecture diagrams&lt;br&gt;
🎨 Create a cover image for your dev.to article&lt;/p&gt;

&lt;p&gt;Just tell me!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>📚Enterprise Design Patterns: Table Module with Python Example</title>
      <dc:creator>Walter Emmanuel Salas Jimenez</dc:creator>
      <pubDate>Wed, 05 Nov 2025 18:10:26 +0000</pubDate>
      <link>https://forem.com/wsalas651/enterprise-design-patterns-table-module-with-python-example-5gkn</link>
      <guid>https://forem.com/wsalas651/enterprise-design-patterns-table-module-with-python-example-5gkn</guid>
      <description>&lt;h2&gt;
  
  
  📑 Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Why Table Module Matters&lt;/li&gt;
&lt;li&gt;What is Table Module?&lt;/li&gt;
&lt;li&gt;Analogy: The School Office&lt;/li&gt;
&lt;li&gt;Python Example: Orders Table&lt;/li&gt;
&lt;li&gt;When to Use &amp;amp; When to Avoid&lt;/li&gt;
&lt;li&gt;Curiosities &amp;amp; Recommendations&lt;/li&gt;
&lt;li&gt;GitHub Repo + Automation&lt;/li&gt;
&lt;li&gt;Final Thoughts&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🔥 Why Table Module Matters
&lt;/h2&gt;

&lt;p&gt;In enterprise apps, &lt;strong&gt;business logic&lt;/strong&gt; can live in different places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the &lt;strong&gt;database&lt;/strong&gt; (stored procedures).&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;domain models&lt;/strong&gt; (one class per row).&lt;/li&gt;
&lt;li&gt;Or in a &lt;strong&gt;table module&lt;/strong&gt;: one class per table.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Table Module keeps all rules for a table in a &lt;strong&gt;single place&lt;/strong&gt;, making code easier to read and maintain when logic is simple.&lt;/p&gt;




&lt;h2&gt;
  
  
  📖 What is Table Module?
&lt;/h2&gt;

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

&lt;blockquote&gt;
&lt;p&gt;A single class that handles the business logic for &lt;strong&gt;all rows&lt;/strong&gt; in a database table or view.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;⚖️ Difference with &lt;strong&gt;Domain Model&lt;/strong&gt;:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Domain Model → 1 class per &lt;strong&gt;row&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Table Module → 1 class per &lt;strong&gt;table&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧩 Analogy: The School Office
&lt;/h2&gt;

&lt;p&gt;Imagine a &lt;strong&gt;school secretary’s office&lt;/strong&gt;:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instead of every teacher managing attendance (Domain Model),
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;office&lt;/strong&gt; manages attendance records for the &lt;strong&gt;whole school&lt;/strong&gt; (Table Module).
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📌 Centralized, consistent, and simple.&lt;/p&gt;




&lt;h2&gt;
  
  
  💻 Python Example: Orders Table
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ❌ Without Table Module (spaghetti logic)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;

&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:memory:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CREATE TABLE orders (id INTEGER, customer TEXT, total REAL)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executemany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSERT INTO orders VALUES (?, ?, ?)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;120.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bob&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;80.5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;45.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# Logic spread everywhere 😓
&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT SUM(total) FROM orders WHERE customer=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Alice&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alice total:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;❌ Problem: Logic is scattered in SQL queries all over the app.&lt;/p&gt;




&lt;h3&gt;
  
  
  ✅ With Table Module
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrdersTable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;total_sales&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT SUM(total) FROM orders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sales_by_customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT SUM(total) FROM orders WHERE customer=?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Setup DB
&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:memory:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CREATE TABLE orders (id INTEGER, customer TEXT, total REAL)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executemany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSERT INTO orders VALUES (?, ?, ?)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;120.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bob&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;80.5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;45.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# Usage
&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OrdersTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Total Sales:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;total_sales&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alice Sales:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sales_by_customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;SRP&lt;/strong&gt;: All business logic lives in &lt;code&gt;OrdersTable&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Reusability&lt;/strong&gt;: Centralized methods.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;KISS&lt;/strong&gt;: Simple &amp;amp; clean.  &lt;/p&gt;


&lt;h2&gt;
  
  
  🤔 When to Use &amp;amp; When to Avoid
&lt;/h2&gt;

&lt;p&gt;✅ Use Table Module when:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Business rules are simple.
&lt;/li&gt;
&lt;li&gt;You need reports or aggregations.
&lt;/li&gt;
&lt;li&gt;Tables are the main unit of work.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;❌ Avoid when:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each row has complex behavior.
&lt;/li&gt;
&lt;li&gt;You need polymorphism → prefer Domain Model.
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🧠 Curiosities &amp;amp; Recommendations
&lt;/h2&gt;

&lt;p&gt;💡 Did you know?  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Table Module was a stepping stone before modern ORMs like SQLAlchemy or Django ORM.
&lt;/li&gt;
&lt;li&gt;Many legacy apps still hide Table Modules inside stored procedures.
&lt;/li&gt;
&lt;li&gt;Fowler recommends it for reporting systems.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Pro tip: Use Table Module for data-centric apps, Domain Model for behavior-rich apps.  &lt;/p&gt;


&lt;h2&gt;
  
  
  📦 GitHub Repo + Automation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Repo structure:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;enterprise-table-module/
├─ table_module.py
├─ tests/
│   └─ test_table_module.py
├─ requirements.txt
└─ .github/
   └─ workflows/
      └─ ci.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;requirements.txt&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pytest==8.3.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;.github/workflows/ci.yml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.11'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pip install -r requirements.txt&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pytest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/Wsalas651/enterprise-table-module_WESJ.git" rel="noopener noreferrer"&gt;🔗 GitHub Repository Example&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;The Table Module pattern is a powerful yet underrated way to structure enterprise apps when logic is simple and table-oriented.&lt;/p&gt;

&lt;p&gt;✨ Remember:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use it for reports and aggregations.
&lt;/li&gt;
&lt;li&gt;Switch to Domain Model when rules grow complex.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✍️ Your turn! Would you use Table Module in your next project?&lt;br&gt;&lt;br&gt;
Let me know in the comments 👇&lt;/p&gt;

</description>
      <category>python</category>
      <category>architecture</category>
      <category>designpatterns</category>
    </item>
    <item>
      <title>📚Enterprise Design Patterns: Table Module with Python Example</title>
      <dc:creator>Walter Emmanuel Salas Jimenez</dc:creator>
      <pubDate>Wed, 17 Sep 2025 07:02:04 +0000</pubDate>
      <link>https://forem.com/wsalas651/enterprise-design-patterns-table-module-with-python-example-2jk9</link>
      <guid>https://forem.com/wsalas651/enterprise-design-patterns-table-module-with-python-example-2jk9</guid>
      <description>&lt;p&gt;🚀 In this article, we’ll explore the &lt;strong&gt;Table Module&lt;/strong&gt; pattern from &lt;em&gt;Martin Fowler’s&lt;/em&gt; &lt;em&gt;Catalog of Enterprise Application Architecture&lt;/em&gt;.&lt;br&gt;&lt;br&gt;
We’ll break it down with analogies, real-world Python code, and a GitHub repo with automation.  &lt;/p&gt;


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

&lt;ol&gt;
&lt;li&gt;Why Table Module Matters&lt;/li&gt;
&lt;li&gt;What is Table Module?&lt;/li&gt;
&lt;li&gt;Analogy: The School Office&lt;/li&gt;
&lt;li&gt;Python Example: Orders Table&lt;/li&gt;
&lt;li&gt;When to Use &amp;amp; When to Avoid&lt;/li&gt;
&lt;li&gt;Curiosities &amp;amp; Recommendations&lt;/li&gt;
&lt;li&gt;GitHub Repo + Automation&lt;/li&gt;
&lt;li&gt;Final Thoughts&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  🔥 Why Table Module Matters
&lt;/h2&gt;

&lt;p&gt;In enterprise apps, &lt;strong&gt;business logic&lt;/strong&gt; can live in different places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the &lt;strong&gt;database&lt;/strong&gt; (stored procedures).&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;domain models&lt;/strong&gt; (one class per row).&lt;/li&gt;
&lt;li&gt;Or in a &lt;strong&gt;table module&lt;/strong&gt;: one class per table.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Table Module keeps all rules for a table in a &lt;strong&gt;single place&lt;/strong&gt;, making code easier to read and maintain when logic is simple.&lt;/p&gt;


&lt;h2&gt;
  
  
  📖 What is Table Module?
&lt;/h2&gt;

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

&lt;blockquote&gt;
&lt;p&gt;A single class that handles the business logic for &lt;strong&gt;all rows&lt;/strong&gt; in a database table or view.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;⚖️ Difference with &lt;strong&gt;Domain Model&lt;/strong&gt;:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Domain Model → 1 class per &lt;strong&gt;row&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Table Module → 1 class per &lt;strong&gt;table&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🧩 Analogy: The School Office
&lt;/h2&gt;

&lt;p&gt;Imagine a &lt;strong&gt;school secretary’s office&lt;/strong&gt;:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instead of every teacher managing attendance (Domain Model),
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;office&lt;/strong&gt; manages attendance records for the &lt;strong&gt;whole school&lt;/strong&gt; (Table Module).
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📌 Centralized, consistent, and simple.&lt;/p&gt;


&lt;h2&gt;
  
  
  💻 Python Example: Orders Table
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ❌ Without Table Module (spaghetti logic)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;

&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:memory:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CREATE TABLE orders (id INTEGER, customer TEXT, total REAL)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executemany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSERT INTO orders VALUES (?, ?, ?)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;120.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bob&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;80.5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;45.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# Logic spread everywhere 😓
&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT SUM(total) FROM orders WHERE customer=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Alice&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alice total:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;❌ Problem: Logic is scattered in SQL queries all over the app.&lt;/p&gt;


&lt;h3&gt;
  
  
  ✅ With Table Module
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrdersTable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;total_sales&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT SUM(total) FROM orders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sales_by_customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT SUM(total) FROM orders WHERE customer=?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Setup DB
&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:memory:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CREATE TABLE orders (id INTEGER, customer TEXT, total REAL)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executemany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSERT INTO orders VALUES (?, ?, ?)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;120.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bob&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;80.5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;45.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# Usage
&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OrdersTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Total Sales:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;total_sales&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alice Sales:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sales_by_customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;✅ &lt;strong&gt;SRP&lt;/strong&gt;: All business logic lives in &lt;code&gt;OrdersTable&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Reusability&lt;/strong&gt;: Centralized methods.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;KISS&lt;/strong&gt;: Simple &amp;amp; clean.  &lt;/p&gt;


&lt;h2&gt;
  
  
  🤔 When to Use &amp;amp; When to Avoid
&lt;/h2&gt;

&lt;p&gt;✅ Use Table Module when:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Business rules are simple.
&lt;/li&gt;
&lt;li&gt;You need reports or aggregations.
&lt;/li&gt;
&lt;li&gt;Tables are the main unit of work.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;❌ Avoid when:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each row has complex behavior.
&lt;/li&gt;
&lt;li&gt;You need polymorphism → prefer Domain Model.
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🧠 Curiosities &amp;amp; Recommendations
&lt;/h2&gt;

&lt;p&gt;💡 Did you know?  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Table Module was a stepping stone before modern ORMs like SQLAlchemy or Django ORM.
&lt;/li&gt;
&lt;li&gt;Many legacy apps still hide Table Modules inside stored procedures.
&lt;/li&gt;
&lt;li&gt;Fowler recommends it for reporting systems.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Pro tip: Use Table Module for data-centric apps, Domain Model for behavior-rich apps.  &lt;/p&gt;


&lt;h2&gt;
  
  
  📦 GitHub Repo + Automation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Repo structure:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;enterprise-table-module/
├─ table_module.py
├─ tests/
│   └─ test_table_module.py
├─ requirements.txt
└─ .github/
   └─ workflows/
      └─ ci.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;requirements.txt&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pytest==8.3.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;.github/workflows/ci.yml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.11'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pip install -r requirements.txt&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pytest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/Wsalas651/enterprise-table-module_WESJ.git" rel="noopener noreferrer"&gt;🔗 GitHub Repository Example&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;The Table Module pattern is a powerful yet underrated way to structure enterprise apps when logic is simple and table-oriented.&lt;/p&gt;

&lt;p&gt;✨ Remember:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use it for reports and aggregations.
&lt;/li&gt;
&lt;li&gt;Switch to Domain Model when rules grow complex.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✍️ Your turn! Would you use Table Module in your next project?&lt;br&gt;&lt;br&gt;
Let me know in the comments 👇&lt;/p&gt;

</description>
      <category>python</category>
      <category>architecture</category>
      <category>designpatterns</category>
    </item>
    <item>
      <title>🧩 Design Principles of Software: Building Better Code with Python</title>
      <dc:creator>Walter Emmanuel Salas Jimenez</dc:creator>
      <pubDate>Wed, 17 Sep 2025 05:37:59 +0000</pubDate>
      <link>https://forem.com/wsalas651/design-principles-of-software-building-better-code-with-python-1cl0</link>
      <guid>https://forem.com/wsalas651/design-principles-of-software-building-better-code-with-python-1cl0</guid>
      <description>&lt;h1&gt;
  
  
  🚀 TL;DR
&lt;/h1&gt;

&lt;p&gt;This article explains key &lt;strong&gt;software design principles (SOLID + DRY +&lt;br&gt;
KISS)&lt;/strong&gt; with a real-world &lt;strong&gt;Python example&lt;/strong&gt;.\&lt;br&gt;
You'll also find step-by-step code, a GitHub repo with automation, and a&lt;br&gt;
short guide to present this as a &lt;strong&gt;5-minute video&lt;/strong&gt;. 🎥&lt;/p&gt;


&lt;h2&gt;
  
  
  📌 Outline
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; Why software design principles matter 🤔\&lt;/li&gt;
&lt;li&gt; Key principles (SOLID, DRY, KISS, YAGNI) 📚\&lt;/li&gt;
&lt;li&gt; Python example: Order Processing System 🐍\&lt;/li&gt;
&lt;li&gt; Refactoring with design principles applied ✨\&lt;/li&gt;
&lt;li&gt; GitHub repo structure &amp;amp; CI/CD automation ⚙️&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  1️⃣ Why software design principles matter 🤔
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  🔧 &lt;strong&gt;Maintainability&lt;/strong&gt; → Easier to update and fix bugs.\&lt;/li&gt;
&lt;li&gt;  ♻️ &lt;strong&gt;Reusability&lt;/strong&gt; → Write once, use multiple times.\&lt;/li&gt;
&lt;li&gt;  📈 &lt;strong&gt;Scalability&lt;/strong&gt; → Design that grows with your app.\&lt;/li&gt;
&lt;li&gt;  👩‍💻 &lt;strong&gt;Team collaboration&lt;/strong&gt; → Principles help everyone understand and
extend code.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  2️⃣ Core Design Principles 📚
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;SOLID&lt;/strong&gt; → Five core OOP principles:

&lt;ul&gt;
&lt;li&gt;  S: Single Responsibility\&lt;/li&gt;
&lt;li&gt;  O: Open/Closed\&lt;/li&gt;
&lt;li&gt;  L: Liskov Substitution\&lt;/li&gt;
&lt;li&gt;  I: Interface Segregation\&lt;/li&gt;
&lt;li&gt;  D: Dependency Inversion&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;DRY&lt;/strong&gt; → Don't Repeat Yourself\&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;KISS&lt;/strong&gt; → Keep It Simple, Stupid\&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;YAGNI&lt;/strong&gt; → You Ain't Gonna Need It&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  3️⃣ Python Example: Order Processing System 🐍
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ❌ Bad code (violates SRP, DRY):
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calculate_total&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_to_db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Saving order to database...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sending email to customer...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;👉 &lt;strong&gt;Problem:&lt;/strong&gt; One class is doing everything (violates SRP).&lt;/p&gt;


&lt;h3&gt;
  
  
  ✅ Refactored code with design principles:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calculate_total&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderRepository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Saving order with total &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculate_total&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; to database...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_order_confirmation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Email sent for order total: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculate_total&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# Usage
&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Pizza&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Cola&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OrderRepository&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EmailService&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_order_confirmation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;✅ &lt;strong&gt;SRP&lt;/strong&gt;: Each class has a single responsibility.\&lt;br&gt;
✅ &lt;strong&gt;DRY&lt;/strong&gt;: No duplicate calculation logic.\&lt;br&gt;
✅ &lt;strong&gt;KISS&lt;/strong&gt;: Simple methods and classes.\&lt;br&gt;
✅ &lt;strong&gt;Extensible&lt;/strong&gt;: Swap EmailService with SMS later (Open/Closed&lt;br&gt;
Principle).&lt;/p&gt;


&lt;h2&gt;
  
  
  4️⃣ Repo Structure &amp;amp; Automation ⚙️
&lt;/h2&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;software-design-principles/
├─ order.py
├─ tests/
│   └─ test_order.py
├─ requirements.txt
├─ README.md
└─ .github/
   └─ workflows/
      └─ ci.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;requirements.txt&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pytest==8.3.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;ci.yml (GitHub Actions)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.11'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pip install -r requirements.txt&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pytest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;🚀Repository link&lt;/strong&gt;&lt;br&gt;
      🔑&lt;a href="https://github.com/Wsalas651/solid-design-patterns-python_WESJ.git" rel="noopener noreferrer"&gt;Repository to Practice&lt;/a&gt;&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>design</category>
      <category>programming</category>
      <category>solidprinciples</category>
    </item>
    <item>
      <title>📊 Other Dashboard Tools: Streamlit, Dash, and Bokeh (with deploy and CI/CD)</title>
      <dc:creator>Walter Emmanuel Salas Jimenez</dc:creator>
      <pubDate>Mon, 15 Sep 2025 17:26:19 +0000</pubDate>
      <link>https://forem.com/wsalas651/other-dashboard-tools-streamlit-dash-and-bokeh-with-deploy-and-cicd-4bnd</link>
      <guid>https://forem.com/wsalas651/other-dashboard-tools-streamlit-dash-and-bokeh-with-deploy-and-cicd-4bnd</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Quick Summary:&lt;/strong&gt; In this practical article in Spanish, you'll see three popular alternatives for creating dashboards and reports in Python — &lt;strong&gt;Streamlit&lt;/strong&gt;, &lt;strong&gt;Dash&lt;/strong&gt; y &lt;strong&gt;Bokeh&lt;/strong&gt; — with ready-to-run code examples, step-by-step deployment instructions (including CI/CD with GitHub Actions), a short guide for publishing on Dev.to, and an English script for your presentation video (≤5 min). 🚀&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🧭 Índice
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Why use these tools?&lt;/li&gt;
&lt;li&gt;Streamlit — mini example + simple deploy&lt;/li&gt;
&lt;li&gt;Dash (Plotly) — example, Docker and CI/CD to Cloud Run&lt;/li&gt;
&lt;li&gt;Bokeh — example and deployment notes (websockets)&lt;/li&gt;
&lt;li&gt;Repo structure and requirements.txt&lt;/li&gt;
&lt;li&gt;GitHub Actions: pipeline (tests → build → deploy)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1) Why use these tools? 🤔
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Streamlit&lt;/strong&gt;: ultra-fast for interactive prototypes and demos. Ideal for data scientists. ✅
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dash&lt;/strong&gt;: powerful when you need complex components and fine control (Plotly + Flask). 👍
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bokeh&lt;/strong&gt;: very good for interactive visualizations and apps that require a server (Bokeh Server). 🏗️
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each tool has trade-offs in ease vs control vs scalability — in this article you'll see minimal examples and how to get them to the cloud.&lt;/p&gt;




&lt;h2&gt;
  
  
  2) Streamlit — minimal example
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Archivo:&lt;/strong&gt; &lt;code&gt;streamlit_app.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# streamlit_app.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;streamlit&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;altair&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;alt&lt;/span&gt;

&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Demo: Streamlit - Sales&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;category&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;B&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;C&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;B&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;C&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sales&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;sel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;selectbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Select category&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;All&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;category&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sel&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;All&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;category&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;sel&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;chart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Chart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mark_bar&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;category&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sales&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;altair_chart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;use_container_width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Run locally:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;streamlit pandas altair
streamlit run streamlit_app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Deployment tip:&lt;/strong&gt; The fastest way is to use &lt;strong&gt;Streamlit Community Cloud&lt;/strong&gt; (direct connection with GitHub: you upload your repo and deploy it from &lt;code&gt;share.streamlit.io&lt;/code&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  3) Dash (Plotly) — example + Docker + CI/CD (Cloud Run)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;code&gt;app.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dash&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Dash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dcc&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;plotly.express&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;

&lt;span class="c1"&gt;# Example data
&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;]})&lt;/span&gt;
&lt;span class="n"&gt;fig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Dash Example&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Dash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;  &lt;span class="c1"&gt;# exposes the WSGI app for Gunicorn
&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Div&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;H1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Dash - Demo&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;dcc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Graph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figure&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PORT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8050&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;requirements.txt (example):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dash
pandas
plotly
gunicorn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dockerfile (para Cloud Run u otros servicios)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.11-slim&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="c"&gt;# Gunicorn runs the WSGI server exposed in `server`&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["gunicorn", "--bind", ":$PORT", "--workers", "1", "--threads", "8", "--timeout", "0", "app:server"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GitHub Actions → Build &amp;amp; Deploy to Google Cloud Run
&lt;/h3&gt;

&lt;p&gt;Save this workflow to &lt;code&gt;.github/workflows/deploy-cloudrun.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI/CD - Cloud Run&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;main&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build-and-deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configure GCloud&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;google-github-actions/setup-gcloud@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;service_account_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GCP_SA_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;project_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GCP_PROJECT_ID }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and push image (Cloud Build)&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;gcloud builds submit --tag gcr.io/${{ secrets.GCP_PROJECT_ID }}/dash-app:$GITHUB_SHA&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to Cloud Run&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;google-github-actions/deploy-cloudrun@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dash-app&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/${{ secrets.GCP_PROJECT_ID }}/dash-app:$GITHUB_SHA&lt;/span&gt;
          &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-central1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;What it does:&lt;/strong&gt; On every push to &lt;code&gt;main&lt;/code&gt;, the workflow builds the image, uploads it to Google Container Registry, and deploys a new revision to Cloud Run.&lt;/p&gt;




&lt;h2&gt;
  
  
  4) Bokeh — minimal example and deployment notes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;code&gt;main.py&lt;/code&gt; (Bokeh server)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# main.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bokeh.plotting&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;figure&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bokeh.io&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;curdoc&lt;/span&gt;

&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Bokeh demo&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;line&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="nf"&gt;curdoc&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;add_root&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Run locally (Bokeh server):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;bokeh
bokeh serve &lt;span class="nt"&gt;--show&lt;/span&gt; main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Deployment notes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bokeh uses &lt;strong&gt;websockets&lt;/strong&gt; (Bokeh Server). When deploying, ensure your service supports timeouts and websocket connections.
&lt;/li&gt;
&lt;li&gt;Typical strategy: create a Dockerfile with &lt;code&gt;bokeh serve --port $PORT --allow-websocket-origin='*' main.py&lt;/code&gt; and deploy to Cloud Run (or similar).
&lt;/li&gt;
&lt;li&gt;If using Cloud Run, configure a higher timeout for long connections.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5) Recommended structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-dashboard/
├─ streamlit_app.py
├─ app.py          # dash
├─ main.py         # bokeh
├─ requirements.txt
├─ Dockerfile      # for dash/bokeh if using container
└─ .github/
   └─ workflows/
      └─ deploy-cloudrun.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6) GitHub Actions: suggested pipeline (details)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test&lt;/strong&gt; → Run &lt;code&gt;pytest&lt;/code&gt; or quick checks (&lt;code&gt;flake8&lt;/code&gt;, &lt;code&gt;mypy&lt;/code&gt;).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build&lt;/strong&gt; → Build Docker image or run &lt;code&gt;gcloud builds submit&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy&lt;/strong&gt; → &lt;code&gt;gcloud run deploy&lt;/code&gt; or use &lt;code&gt;google-github-actions/deploy-cloudrun&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;







&lt;h2&gt;
  
  
  🔗 GitHub Repository
&lt;/h2&gt;

&lt;p&gt;The full code with &lt;strong&gt;Streamlit&lt;/strong&gt;, &lt;strong&gt;Dash&lt;/strong&gt;, and &lt;strong&gt;Bokeh&lt;/strong&gt; examples, along with the &lt;code&gt;Dockerfile&lt;/code&gt; and &lt;strong&gt;CI/CD workflows with GitHub Actions&lt;/strong&gt;, is available in the following repository:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/Wsalas651/my-dashboard-for-WESJ" rel="noopener noreferrer"&gt;GitHub Repository: my-dashboard&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>beginners</category>
      <category>devops</category>
      <category>discuss</category>
    </item>
    <item>
      <title>🚀 Applying API Testing Frameworks: Real-World Examples with Code</title>
      <dc:creator>Walter Emmanuel Salas Jimenez</dc:creator>
      <pubDate>Thu, 05 Jun 2025 19:37:46 +0000</pubDate>
      <link>https://forem.com/wsalas651/applying-api-testing-frameworks-real-world-examples-with-code-35jp</link>
      <guid>https://forem.com/wsalas651/applying-api-testing-frameworks-real-world-examples-with-code-35jp</guid>
      <description>&lt;p&gt;API testing is a cornerstone of modern software quality assurance. Whether you're testing RESTful services or SOAP endpoints, having the right framework in your toolbox can make all the difference. In this post, we'll explore how to apply popular API testing frameworks, show real-world examples, and point you to great learning resources, including videos and documentation.&lt;/p&gt;

&lt;p&gt;💡 If you're still evaluating API testing tools, check out this comparison article:&lt;a href="https://alicealdaine.medium.com/top-10-api-testing-tools-rest-soap-services-5395cb03cfa9" rel="noopener noreferrer"&gt;Top 10 API Testing Tools&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📌 Why API Testing Matters&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Detects defects early&lt;/li&gt;
&lt;li&gt;✅ Ensures contract compliance&lt;/li&gt;
&lt;li&gt;✅ Supports automation pipelines&lt;/li&gt;
&lt;li&gt;✅ Works independently of the UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Frameworks like Postman, REST Assured, Karate, Supertest, and Newman offer varied features like scripting, CI/CD integration, and mocking support.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔧 1. REST Assured (Java)&lt;/strong&gt;&lt;br&gt;
Best for: Java-based automation frameworks&lt;br&gt;
Supports: RESTful APIs&lt;/p&gt;

&lt;p&gt;&lt;em&gt;✨ Setup (Maven)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;io.rest-assured&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;rest-assured&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;5.3.0&amp;lt;/version&amp;gt;
    &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;✅ Sample Test&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;

import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

class APITest {

    @Test
    void checkUserAPI() {
        RestAssured.baseURI = "https://jsonplaceholder.typicode.com";

        given()
            .when()
            .get("/users/1")
            .then()
            .statusCode(200)
            .body("username", equalTo("Bret"));
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📘 Docs: &lt;a href="https://rest-assured.io/" rel="noopener noreferrer"&gt;rest-assured.io&lt;/a&gt;&lt;br&gt;
🎥 Video: &lt;a href="https://www.youtube.com/watch?v=7YcW25PHnAA" rel="noopener noreferrer"&gt;REST Assured Full Course (YouTube)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🥋 2. Karate DSL (Java / DSL)&lt;/strong&gt;&lt;br&gt;
Best for: BDD-style testing&lt;br&gt;
Supports: REST, SOAP, GraphQL, gRPC&lt;br&gt;
_&lt;br&gt;
✨ Setup (Maven)_&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
  &amp;lt;groupId&amp;gt;com.intuit.karate&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;karate-junit5&amp;lt;/artifactId&amp;gt;
  &amp;lt;version&amp;gt;1.4.1&amp;lt;/version&amp;gt;
  &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;✅ Feature File Example&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Feature: Get a single user

Scenario: Validate user details
  Given url 'https://jsonplaceholder.typicode.com/users/1'
  When method GET
  Then status 200
  And match response.username == 'Bret'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📘 Docs: &lt;a href="https://www.karatelabs.io/" rel="noopener noreferrer"&gt;karatelabs.io&lt;/a&gt;&lt;br&gt;
🎥 Video: &lt;a href="https://www.youtube.com/watch?v=CSaBRZ9a2L4&amp;amp;list=PL8VbCbavWfeGGVl-82ZU1aLNDuN0MNgxh" rel="noopener noreferrer"&gt;Karate DSL Tutorial (YouTube)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🧪 3. Supertest (JavaScript)&lt;/strong&gt;&lt;br&gt;
Best for: Node.js apps&lt;br&gt;
Works with: Express.js, Mocha, Jest&lt;/p&gt;

&lt;p&gt;&lt;em&gt;✨ Install&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save-dev supertest jest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;✅ Sample Test (with Jest)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const request = require('supertest');
const app = require('../app'); // Your Express app

describe('GET /users/1', () =&amp;gt; {
  it('should return a user', async () =&amp;gt; {
    const res = await request(app).get('/users/1');
    expect(res.statusCode).toBe(200);
    expect(res.body.username).toBe('Bret');
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📘 Docs: &lt;a href="https://github.com/ladjs/supertest" rel="noopener noreferrer"&gt;Supertest GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📫 4. Postman + Newman (JavaScript)&lt;/strong&gt;&lt;br&gt;
Best for: Manual + Automated tests&lt;br&gt;
Newman: CLI runner for Postman collections&lt;/p&gt;

&lt;p&gt;&lt;em&gt;✅ Use Case: CI/CD Integration&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create your collection in Postman.&lt;/li&gt;
&lt;li&gt;Export as collection.json.&lt;/li&gt;
&lt;li&gt;Run in CI pipeline:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;newman run collection.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📘 Docs: &lt;a href="https://www.npmjs.com/package/newman" rel="noopener noreferrer"&gt;Newman CLI&lt;/a&gt;&lt;br&gt;
🎥 Video: &lt;a href="https://www.youtube.com/watch?v=GuGPC29Nf7c&amp;amp;pp=ygUcUG9zdG1hbiArIE5ld21hbiBDSSBUdXRvcmlhbA%3D%3D" rel="noopener noreferrer"&gt;Postman + Newman CI Tutorial&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;🛠️ Bonus: Best Practices&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔁 Use data-driven tests (CSV, JSON)&lt;/li&gt;
&lt;li&gt;🔐 Secure sensitive data (use .env files or vaults)&lt;/li&gt;
&lt;li&gt;🧪 Automate in CI/CD using GitHub Actions, GitLab CI, Jenkins&lt;/li&gt;
&lt;li&gt;📄 Generate reports (Allure, Newman HTML Reporter)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🙌 Final Thoughts&lt;/strong&gt;&lt;br&gt;
API testing is no longer optional—it's essential. Start small, automate progressively, and pick a framework that aligns with your team’s tech stack and workflow. Whether you're coding in Java or JavaScript, there's a tool to fit your needs.&lt;/p&gt;

&lt;p&gt;If you liked this article, don’t forget to:&lt;/p&gt;

&lt;p&gt;👉 Bookmark it&lt;br&gt;
🗣️ Share it with your QA/dev team&lt;br&gt;
💬 Comment your favorite API testing tool&lt;/p&gt;

</description>
    </item>
    <item>
      <title>🔐IaC Security Made Easy with Horusec: A SAST Approach🚀</title>
      <dc:creator>Walter Emmanuel Salas Jimenez</dc:creator>
      <pubDate>Mon, 21 Apr 2025 02:23:18 +0000</pubDate>
      <link>https://forem.com/wsalas651/iac-security-made-easy-with-horusec-a-sast-approach-523</link>
      <guid>https://forem.com/wsalas651/iac-security-made-easy-with-horusec-a-sast-approach-523</guid>
      <description>&lt;p&gt;&lt;strong&gt;🧩 Introduction&lt;/strong&gt;&lt;br&gt;
Infrastructure as Code (IaC) has transformed how teams provision and manage infrastructure. By treating infrastructure configurations like code, we gain consistency, speed, and repeatability. However, with this agility comes the responsibility of securing that code — just like we do with traditional application code. That’s where SAST tools come in. In this article, we’ll explore how to apply Static Application Security Testing (SAST) using Horusec to scan IaC codebases and keep your infrastructure safe 🛡️.&lt;/p&gt;

&lt;p&gt;In this article, we’ll learn how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up Horusec to scan any IaC codebase.&lt;/li&gt;
&lt;li&gt;Apply it locally and in CI/CD.&lt;/li&gt;
&lt;li&gt;Use it with other SAST tools.&lt;/li&gt;
&lt;li&gt;Automate reporting and exports.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;💡 What is Horusec?&lt;/strong&gt;&lt;br&gt;
Horusec is a powerful, open-source security tool that performs static analysis across multiple languages and IaC formats (like Terraform and Kubernetes YAML files). It's designed to find vulnerabilities before code reaches production 🏭.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚙️ Key Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🌐 Multi-language &amp;amp; IaC support (Terraform, Dockerfile, YAML, etc.)&lt;/li&gt;
&lt;li&gt;🔄 CI/CD integration with GitHub Actions, GitLab CI, Jenkins, and others&lt;/li&gt;
&lt;li&gt;📊 Clear vulnerability reports with severity levels and recommendations&lt;/li&gt;
&lt;li&gt;🧠 Developer-friendly: runs locally or in pipelines without complex setup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;📥 Installing Horusec CLI&lt;/strong&gt;&lt;br&gt;
You can install Horusec using a simple script:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl -fsSL https://raw.githubusercontent.com/ZupIT/horusec/main/deployments/scripts/install.sh | bash -s latest&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Or via Docker 🐳:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker run -v /var/run/docker.sock:/var/run/docker.sock -v $(pwd):/src horuszup/horusec-cli:latest horusec start -p&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔍 Scanning Infrastructure as Code&lt;/strong&gt;&lt;br&gt;
Once installed, scanning your code is as easy as running:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;horusec start -p .&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Horusec will inspect all supported files in the current directory and display a summary of issues found 📋.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚡ Integrating into CI/CD (GitHub Actions Example)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s automate this! Here's how to run Horusec as part of your GitHub Actions pipeline:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;name: Horusec Security Scan&lt;/code&gt;&lt;br&gt;
&lt;code&gt;on:&lt;/code&gt;&lt;br&gt;
&lt;code&gt;push:&lt;br&gt;
    branches: [ main ]&lt;br&gt;
  pull_request:&lt;br&gt;
    branches: [ main ]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;jobs:&lt;br&gt;
  horusec_scan:&lt;br&gt;
    runs-on: ubuntu-latest&lt;br&gt;
    steps:&lt;br&gt;
      - uses: actions/checkout@v2&lt;br&gt;
      - name: Install Horusec&lt;br&gt;
        run: |&lt;br&gt;
          curl -fsSL https://raw.githubusercontent.com/ZupIT/horusec/main/deployments/scripts/install.sh | bash -s latest&lt;/code&gt;&lt;br&gt;
      &lt;code&gt;- name: Run Horusec Scan&lt;br&gt;
        run: |&lt;br&gt;
          horusec start -p . -e="true"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This setup scans your repo on every push or pull request to main, helping you catch security issues early 🚨.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🧪 Example: Terraform (Without docker)&lt;/strong&gt;&lt;br&gt;
You can see the example in my repository on Github.&lt;/p&gt;

&lt;p&gt;👉&lt;a href="https://github.com/Wsalas651/horusec-iac-demo" rel="noopener noreferrer"&gt;horusec-iac-demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just run &lt;code&gt;horusec start -p&lt;/code&gt; . and let it detect issues like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hardcoded secrets 🔑&lt;/li&gt;
&lt;li&gt;Insecure resource definitions 🔓&lt;/li&gt;
&lt;li&gt;Misconfigured ports or permissions 🔐&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;📊 Analyzing the Results&lt;/strong&gt;&lt;br&gt;
Horusec categorizes vulnerabilities by severity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🟥 Critical&lt;/li&gt;
&lt;li&gt;🟧 High&lt;/li&gt;
&lt;li&gt;🟨 Medium&lt;/li&gt;
&lt;li&gt;🟩 Low&lt;/li&gt;
&lt;li&gt;🟦 Info&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each issue includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File and line number 📍&lt;/li&gt;
&lt;li&gt;Description and recommendation 📘&lt;/li&gt;
&lt;li&gt;CWE ID (Common Weakness Enumeration) for deeper learning 🔍&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;📊 Reports &amp;amp; Outputs&lt;/strong&gt;&lt;br&gt;
Horusec supports multiple output formats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;json – for custom parsing or dashboards.&lt;/li&gt;
&lt;li&gt;html – for visual inspection.&lt;/li&gt;
&lt;li&gt;sarif – compatible with GitHub Security tab.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example to export an HTML report:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;horusec start -p . -o html -O report.html&lt;/code&gt;&lt;br&gt;
Open &lt;code&gt;report.html&lt;/code&gt; in any browser to see a full security summary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Best Practices&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔁 Run Horusec on each commit or pull request&lt;/li&gt;
&lt;li&gt;🚫 Block merge if critical vulnerabilities are found&lt;/li&gt;
&lt;li&gt;📚 Train your team to understand IaC security risks&lt;/li&gt;
&lt;li&gt;🧩 Use Horusec alongside other tools (e.g., Snyk, Checkov)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🔗 References&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ZupIT/horusec" rel="noopener noreferrer"&gt;Horusec GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.horusec.io" rel="noopener noreferrer"&gt;Horusec Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.hashicorp.com/tutorials/terraform/security" rel="noopener noreferrer"&gt;Terraform Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/code-security" rel="noopener noreferrer"&gt;GitHub Actions SARIF Upload&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🧠 Conclusion&lt;/strong&gt;&lt;br&gt;
IaC enables rapid infrastructure deployment — but speed without security is a recipe for risk. Using Horusec as part of your SAST approach allows you to catch vulnerabilities early, automate scans, and ensure compliance across your entire infrastructure pipeline. Give it a try and secure your code from commit to cloud 🌩️🔒&lt;/p&gt;

&lt;p&gt;Have you tried it yet? Got any questions? Drop them in the comments below — I’d love to hear from you! 👇&lt;/p&gt;

</description>
    </item>
    <item>
      <title>🛡️ Scan and Protect Any App in 5 Minutes with Bearer CLI (SAST for Everyone)</title>
      <dc:creator>Walter Emmanuel Salas Jimenez</dc:creator>
      <pubDate>Sun, 20 Apr 2025 18:43:33 +0000</pubDate>
      <link>https://forem.com/wsalas651/scan-and-protect-any-app-in-5-minutes-with-bearer-cli-sast-for-everyone-3n7</link>
      <guid>https://forem.com/wsalas651/scan-and-protect-any-app-in-5-minutes-with-bearer-cli-sast-for-everyone-3n7</guid>
      <description>&lt;p&gt;Applying security testing to your application shouldn't be complicated. In this guide, I’ll show you how to use a powerful tool called Bearer CLI to run static application security tests (SAST) easily, efficiently, and with compatibility for almost any application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🧠 What is SAST?&lt;/strong&gt;&lt;br&gt;
SAST (Static Application Security Testing) is a technique for analyzing the source code of an application to find vulnerabilities without executing it. It’s a key part of any modern DevSecOps pipeline.&lt;/p&gt;

&lt;p&gt;With SAST, you can detect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQL Injection&lt;/li&gt;
&lt;li&gt;XSS (Cross-site Scripting)&lt;/li&gt;
&lt;li&gt;Sensitive data leaks&lt;/li&gt;
&lt;li&gt;Insecure use of libraries, and more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🐻 What is Bearer CLI?&lt;/strong&gt;&lt;br&gt;
Bearer CLI is an open-source tool that scans your code for security vulnerabilities and privacy issues. Unlike many SAST tools that require complex configuration, Bearer CLI works right out of the box via your terminal—no initial setup required.&lt;/p&gt;

&lt;p&gt;✅ Open Source&lt;br&gt;
✅ Supports multiple languages (JavaScript, TypeScript, Java, Ruby, etc.)&lt;br&gt;
✅ Easy integration with CI/CD pipelines (GitHub Actions, GitLab CI, etc.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🚀 How to Install Bearer CLI&lt;/strong&gt;&lt;br&gt;
Install using Homebrew (on macOS or Linux):&lt;/p&gt;

&lt;p&gt;&lt;code&gt;brew install bearer/tap/bearer&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
Or use the official install script:&lt;br&gt;
&lt;code&gt;curl -sfL https://raw.githubusercontent.com/Bearer/bearer/main/install.sh | sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🛠️  Scan Your Application&lt;/strong&gt;&lt;br&gt;
Once installed, navigate to the root of your project and simply run:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bearer scan .&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This command scans your codebase and prints a vulnerability report directly in your terminal.&lt;/p&gt;

&lt;p&gt;🔍 Example usage:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd ~/mis-proyectos/mi-app&lt;/code&gt;&lt;br&gt;
&lt;code&gt;bearer scan .&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🧩 Practical Example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;See a working demo in this GitHub repository:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Wsalas651/bearer-cli-demo" rel="noopener noreferrer"&gt;Example Bearer CLI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📦 CI/CD Integration&lt;/strong&gt;&lt;br&gt;
To run Bearer automatically in your CI pipelines, add it as a step in your GitHub Actions .yml file:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;- name: Scan with Bearer&lt;br&gt;
  run: |&lt;br&gt;
    curl -sfL https://raw.githubusercontent.com/Bearer/bearer/main/install.sh | sh&lt;br&gt;
    ./bin/bearer scan .&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Or in GitLab CI/CD:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sast_scan:&lt;br&gt;
  script:&lt;br&gt;
    - curl -sfL https://raw.githubusercontent.com/Bearer/bearer/main/install.sh | sh&lt;br&gt;
    - ./bin/bearer scan .&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📁 Custom Reports&lt;/strong&gt;&lt;br&gt;
Export results in JSON or SARIF format to integrate with GitHub Security or other tools:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bearer scan . --format sarif &amp;gt; resultado.sarif&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🌐 Resources &amp;amp; References&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🌍 Official Bearer CLI Site: &lt;a href="https://www.bearer.com/cli" rel="noopener noreferrer"&gt;https://www.bearer.com/cli&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📚 Full Documentation: &lt;a href="https://docs.bearer.com/cli" rel="noopener noreferrer"&gt;https://docs.bearer.com/cli&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🧰 GitHub Repository: &lt;a href="https://github.com/Bearer/bearer" rel="noopener noreferrer"&gt;https://github.com/Bearer/bearer&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🎥 Video: Bearer CLI en acción&lt;/strong&gt;&lt;br&gt;
Prefer to learn visually? Check out this awesome video tutorial:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.youtube.com/watch?v=I3o6Vb-SdtA&amp;amp;t=54s&amp;amp;pp=ygUUQmVhcmVyIENMSSBzZWd1cmlkYWQ%3D" rel="noopener noreferrer"&gt;fix your code with Bearer with one click&lt;/a&gt;&lt;br&gt;
 (YouTube)&lt;br&gt;
(📽️ Duration: 14 minutes – super clear and straight to the point!)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ Conclusion&lt;/strong&gt;&lt;br&gt;
With tools like Bearer CLI, security doesn't have to be hard or expensive. Adding SAST scans to your workflow is now just a matter of minutes.&lt;/p&gt;

&lt;p&gt;Have you tried it yet? Got any questions? Drop them in the comments below — I’d love to hear from you! 👇&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
