<?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: Lucas Reis</title>
    <description>The latest articles on Forem by Lucas Reis (@reislucaz).</description>
    <link>https://forem.com/reislucaz</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%2F971416%2F1c565b64-6640-47c8-a534-00c7652be582.png</url>
      <title>Forem: Lucas Reis</title>
      <link>https://forem.com/reislucaz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/reislucaz"/>
    <language>en</language>
    <item>
      <title>Auditing in Java systems: RLS in the database or application-level control?</title>
      <dc:creator>Lucas Reis</dc:creator>
      <pubDate>Sun, 08 Mar 2026 21:37:13 +0000</pubDate>
      <link>https://forem.com/reislucaz/auditing-in-java-systems-rls-in-the-database-or-application-level-control-26e</link>
      <guid>https://forem.com/reislucaz/auditing-in-java-systems-rls-in-the-database-or-application-level-control-26e</guid>
      <description>&lt;p&gt;This question comes up in every project dealing with sensitive data. And the honest answer is: it depends — but there are clear criteria to decide.&lt;/p&gt;

&lt;p&gt;Let me get straight to the point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is RLS-based auditing?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Row Level Security is a native PostgreSQL feature that filters and restricts row access directly in the database, using declarative policies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Enable RLS on the table&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;transactions&lt;/span&gt; &lt;span class="n"&gt;ENABLE&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt; &lt;span class="k"&gt;LEVEL&lt;/span&gt; &lt;span class="k"&gt;SECURITY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;transactions&lt;/span&gt; &lt;span class="k"&gt;FORCE&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt; &lt;span class="k"&gt;LEVEL&lt;/span&gt; &lt;span class="k"&gt;SECURITY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Policy: user only sees records from their own company&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;POLICY&lt;/span&gt; &lt;span class="n"&gt;tenant_isolation&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;transactions&lt;/span&gt;
  &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;company_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app.tenant_id'&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Audit trigger that captures session context&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;fn_audit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;audit_log&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;table_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data_before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data_after&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;TG_TABLE_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TG_OP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;current_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app.user_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;TG_OP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'DELETE'&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;to_jsonb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;TG_OP&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'DELETE'&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;to_jsonb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Auditing happens at the database level. It doesn’t matter where the query came from — psql, DBeaver, the application — the trigger logs everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is application-level control with Spring?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here, the responsibility for filtering and auditing sits in Java code, typically using Spring Security + AOP or interceptors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Interceptor that injects the tenant into context&lt;/span&gt;
&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TenantInterceptor&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;HandlerInterceptor&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;preHandle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpServletRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                             &lt;span class="nc"&gt;HttpServletResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                             &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;tenantId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getHeader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X-Tenant-ID"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;TenantContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ThreadLocal&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;afterCompletion&lt;/span&gt;&lt;span class="o"&gt;(...)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;TenantContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;clear&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Prevents leaking between requests&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Repository that explicitly applies the filter&lt;/span&gt;
&lt;span class="nd"&gt;@Repository&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TransactionRepository&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Transaction&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;tenantId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TenantContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// easy to forget this&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;entityManager&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT t FROM Transaction t WHERE t.companyId = :tenantId"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tenantId"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getResultList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Audit aspect via AOP&lt;/span&gt;
&lt;span class="nd"&gt;@Aspect&lt;/span&gt;
&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuditAspect&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@AfterReturning&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;pointcut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"@annotation(Auditable)"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;returning&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"result"&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;audit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JoinPoint&lt;/span&gt; &lt;span class="n"&gt;jp&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;auditService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;record&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;jp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSignature&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
            &lt;span class="nc"&gt;TenantContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
            &lt;span class="nc"&gt;SecurityContextHolder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getContext&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getAuthentication&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Works well — as long as nobody forgets to apply the filter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The blind spot of each approach&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With RLS, the risk is &lt;code&gt;SET LOCAL&lt;/code&gt; outside a transaction — in connection pools like HikariCP, a plain &lt;code&gt;SET&lt;/code&gt; without a transaction can leak between requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// WRONG — leaks in the connection pool&lt;/span&gt;
&lt;span class="n"&gt;jdbcTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SET app.tenant_id = '"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;tenantId&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"'"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// CORRECT — scoped to the transaction&lt;/span&gt;
&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Transaction&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;jdbcTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SET LOCAL app.tenant_id = '"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;tenantId&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"'"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;transactionRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the data is sensitive (financial, healthcare, legal), don’t rely solely on the application layer. Use RLS as an additional security layer and triggers for immutable auditing.&lt;br&gt;
If your business rules are complex and the team is small, start with application-level control — but document the risks and plan to move tenant isolation to the database as the system scales.&lt;br&gt;
The application can have bugs. The database needs to be the last barrier.&lt;/p&gt;

</description>
      <category>java</category>
      <category>rls</category>
      <category>database</category>
      <category>postgres</category>
    </item>
    <item>
      <title>The time at the expense of software quality</title>
      <dc:creator>Lucas Reis</dc:creator>
      <pubDate>Thu, 07 Sep 2023 16:22:14 +0000</pubDate>
      <link>https://forem.com/reislucaz/the-time-at-the-expense-of-software-quality-an0</link>
      <guid>https://forem.com/reislucaz/the-time-at-the-expense-of-software-quality-an0</guid>
      <description>&lt;p&gt;To understand the current landscape of software development, it is essential to look back to the last century, a time when engineers from large companies dedicated years to creating applications before they could be effectively used. This scenario might not have been a problem if all external factors to the technology team were properly aligned, which, in reality, almost never happens.&lt;/p&gt;

&lt;p&gt;During that time, applications were robust, and concepts like UX/UI would only emerge decades later. There was no research, code version control, or automated testing. This situation was, indeed, a time bomb, and in the 1970s, what we now call the Software Crisis emerged.&lt;/p&gt;

&lt;p&gt;In this context, about one-third of projects were canceled, while two-thirds significantly exceeded their estimated budgets. Considering ease of maintenance was not part of the development process, resulting in exorbitant costs for software maintenance.&lt;/p&gt;

&lt;p&gt;It was during this period that the concept of Software Engineering emerged when technology scholars of the time sought ways to make this process more efficient, eliminating flaws and turning it into a solid science.&lt;/p&gt;

&lt;p&gt;Valuing time is not a mistake; today, technology has advanced, and it is possible to build software in less time. However, it becomes a mistake when it compromises quality and maintainability, which are inherently essential for software sustainability.&lt;/p&gt;

&lt;p&gt;And the cost goes beyond software sustainability. Projects that do not prioritize software quality cost more because they consume more time maintaining the software itself than implementing features that would add value to the business. In 2020, the study "The Cost of Poor Software Quality In the US: A 2020 Report," produced by the "Consortium for Information &amp;amp; Software Quality (CISQ)," reported that low-quality software cost US companies $2.08 trillion.&lt;/p&gt;

&lt;p&gt;Deliveries can benefit everyone involved in a project, but quality deliveries are essential for project success.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;[1] FOWLER, Martin - &lt;a href="https://martinfowler.com/articles/is-quality-worth-cost.html" rel="noopener noreferrer"&gt;https://martinfowler.com/articles/is-quality-worth-cost.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[2] DEV Media, &lt;a href="https://www.devmedia.com.br/qualidade-de-software-engenharia-de-software-29/18209" rel="noopener noreferrer"&gt;https://www.devmedia.com.br/qualidade-de-software-engenharia-de-software-29/18209&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[3] PEIXOTO, Diogo - &lt;a href="https://peixotoo.medium.com/qualidade-de-software-manutenibilidade-complexidade-ciclom%C3%A1tica-5fa452791514" rel="noopener noreferrer"&gt;https://peixotoo.medium.com/qualidade-de-software-manutenibilidade-complexidade-ciclomática-5fa452791514&lt;/a&gt;&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>softwarequality</category>
      <category>webdev</category>
    </item>
    <item>
      <title>WebSockets protocol: Innovating your projects with efficiently live communication.</title>
      <dc:creator>Lucas Reis</dc:creator>
      <pubDate>Wed, 25 Jan 2023 02:52:34 +0000</pubDate>
      <link>https://forem.com/reislucaz/websockets-protocol-innovating-your-projects-with-efficiently-live-communication-3526</link>
      <guid>https://forem.com/reislucaz/websockets-protocol-innovating-your-projects-with-efficiently-live-communication-3526</guid>
      <description>&lt;h2&gt;
  
  
  Presentation
&lt;/h2&gt;

&lt;p&gt;Hello, my name is Lucas Reis, I am 19 years old and a Software Engineering student at UNIEvangélica de Goiás, Brazil. To understand this article, it is recommended that you have some knowledge about &lt;a href="https://www.comptia.org/content/guides/what-is-a-network-protocol" rel="noopener noreferrer"&gt;Network Protocols&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/How_the_Web_works#:~:text=The%20browser%20sends%20an%20HTTP,internet%20connection%20using%20TCP%2FIP." rel="noopener noreferrer"&gt;how Web works at background.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am here to introduce you to a powerful network protocol that can enhance your projects - &lt;a href="https://www.rfc-editor.org/rfc/rfc6455" rel="noopener noreferrer"&gt;WebSockets&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is WebSockets?
&lt;/h2&gt;

&lt;p&gt;WebSocket is, at its core, an application protocol, similar to HTTP or FTP. The main difference is that in these other protocols, communication ends after a request-response interaction, while in WebSockets, the communication persists until one of the sides closes the channel. See the diagram below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwuu84bwu8wh0whh3hogj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwuu84bwu8wh0whh3hogj.png" alt="WebSockets message diagram" width="800" height="552"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Origin
&lt;/h2&gt;

&lt;p&gt;This protocol is built on top of TCP/IP, just like HTTP, but it can also be used in conjunction with HTTP to establish an initial connection between the client and server. Its origins can be traced back to December 2011, when the web was transitioning to Web 2.0, the web of interactions. With this protocol, it has been possible to create real-time applications such as web chats, video games, and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handshake HTTP Sample
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd1zdse3jakiq21sycnms.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd1zdse3jakiq21sycnms.png" alt="Screenshot from RFC 6455" width="500" height="282"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What can this be helpful?
&lt;/h2&gt;

&lt;p&gt;WebSockets can be extremely useful in certain situations where you need persistent and bidirectional real-time communication. In such contexts, it can be the best way to create an application that meets these requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Application Sample
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Webchat&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;You can use WebSockets to easily develop a real-time web chat!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Tracker&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;A Tracker can be developed using WebSockets to communicate in real-time between the front-end that sends coordinates to the back-end every 5 seconds or something similar.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Notifications&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;You can use WebSockets to communicate with the client that they have a new update without the need to reload the page or retrieve a specific endpoint with new notifications.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Video Games&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Video Games can be developed using WebSockets as a Framework! It can enable instant communication between players and the server on the web without losing packets or causing lag.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The end!
&lt;/h2&gt;

&lt;p&gt;This protocol is a great way to innovate your projects and show your friends that you know how to use TCP/IP to its full potential without sacrificing network performance! You are welcome to make suggestions for this article or correct any errors. =D&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc6455" rel="noopener noreferrer"&gt;RFC 6455&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
