<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Fernando Fornieles</title>
    <description>The latest articles on DEV Community by Fernando Fornieles (@nandofm).</description>
    <link>https://dev.to/nandofm</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%2F3707424%2F933dae0f-c492-447f-a437-9e08ad34c6b6.jpg</url>
      <title>DEV Community: Fernando Fornieles</title>
      <link>https://dev.to/nandofm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nandofm"/>
    <language>en</language>
    <item>
      <title>Rapid Development of a Weather Dashboard and API Without AI</title>
      <dc:creator>Fernando Fornieles</dc:creator>
      <pubDate>Mon, 06 Apr 2026 18:35:55 +0000</pubDate>
      <link>https://dev.to/nandofm/a-weather-dashboard-api-with-php-and-symfony-2p4l</link>
      <guid>https://dev.to/nandofm/a-weather-dashboard-api-with-php-and-symfony-2p4l</guid>
      <description>&lt;p&gt;Having a weather station is good, having a dashboard and a history of meteorological data is better. So, I decided to implement a web application.&lt;/p&gt;

&lt;p&gt;The challenge was to see how fast the development could be using only the bundles and commands provided by the PHP Symfony framework.&lt;/p&gt;

&lt;p&gt;The project includes the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Dashboard to see the current weather data and some graphs to display the evolution over time.&lt;/li&gt;
&lt;li&gt;An API to allow the weather station to send data&lt;/li&gt;
&lt;li&gt;An admin area to manage the weather data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here, &lt;a href="https://codeberg.org/horrabin/horrameteo-web" rel="noopener noreferrer"&gt;the source code of the project&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;Preparing everything to be ready to start implementing anything is always the most annoying part, at least for me.&lt;/p&gt;

&lt;p&gt;Luckily I have a MariaDB and an Apache server running on my PC with, more or less, the same configuration as my "production" server, a Raspberry Pi 5 that I use to self-host my private cloud.&lt;/p&gt;

&lt;p&gt;Thanks to this, I only had to create the database for the project and download the basic Symfony project using the amazing Symfony CLI tool.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; symfony new weatherdashboard &lt;span class="nt"&gt;--webapp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once done I installed the Symfony apache-pack for better integration of the project with the Apache web server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; composer require symfony/apache-pack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also installed the Doctrine Fixtures bundle to easily create some initial data, like a default user for the admin area.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; composer require orm-fixtures &lt;span class="nt"&gt;--dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last setup step was to configure the database connection in the Symfony .env file.&lt;/p&gt;

&lt;p&gt;Everything was ready to start working on the project!&lt;/p&gt;

&lt;h3&gt;
  
  
  The Meteorological Data Admin
&lt;/h3&gt;

&lt;p&gt;I wanted to manage the weather data received from the station. Why? Simply to be able to delete some weird data that can be received and also for testing purposes.&lt;/p&gt;

&lt;p&gt;The first step was to create the meteorological data entity to store the weather information.&lt;/p&gt;

&lt;p&gt;The MeteoData entity should have the following attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deviceTemperature: float&lt;/li&gt;
&lt;li&gt;temperature: float&lt;/li&gt;
&lt;li&gt;humidity: float&lt;/li&gt;
&lt;li&gt;relativeHumidity: float&lt;/li&gt;
&lt;li&gt;dewpoint: float&lt;/li&gt;
&lt;li&gt;lux: float&lt;/li&gt;
&lt;li&gt;pressure: float&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Creating the entity was as easy as executing the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; bin/console make:entity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The entity is nothing if the database table it maps doesn't exist. Creating the table in the database is as simple as this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; bin/console make:migration
&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; bin/console doctrine:migrations:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last step was to create all the code to manage the meteorological data with this &lt;em&gt;"magic spell"&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; bin/console make:crud
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without typing any code, just with a few commands, I had the Meteorological Data Admin complete and ready to use. &lt;/p&gt;

&lt;h2&gt;
  
  
  Securing Access to the Meteorological Data Admin
&lt;/h2&gt;

&lt;p&gt;An Admin area needs to be private, so I secured access to the Meteorological Data Admin. &lt;/p&gt;

&lt;p&gt;The first thing to do was to create the User entity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; bin/console make:user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, as before, I ran again the migration commands to create the table in the database.&lt;/p&gt;

&lt;p&gt;To create the admin user I decided to use the &lt;em&gt;Doctrine Fixtures&lt;/em&gt; bundle, a useful tool to insert default or initial needed data into the database.&lt;/p&gt;

&lt;p&gt;To do this I had to implement the &lt;em&gt;&lt;a href="https://codeberg.org/horrabin/horrameteo-web/src/branch/main/src/DataFixtures/UserFixtures.php" rel="noopener noreferrer"&gt;UserFixtures&lt;/a&gt;&lt;/em&gt; class manually to create the admin user. There is no command, or at least I don't know it, to "automagically" create a default admin user.&lt;/p&gt;

&lt;p&gt;Once done, using the following command, the admin user was loaded into the database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; bin/console doctrine:fixtures:load &lt;span class="nt"&gt;--append&lt;/span&gt; &lt;span class="nt"&gt;--group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;UserFixtures
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To access the admin area, a login form is required. Creating it is really easy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; bin/console make:auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last step was manual again but simple. I just added the following to the &lt;em&gt;"access_control"&lt;/em&gt; section of the &lt;em&gt;&lt;a href="https://codeberg.org/horrabin/horrameteo-web/src/branch/main/config/packages/security.yaml" rel="noopener noreferrer"&gt;"config/packages/security.yaml"&lt;/a&gt;&lt;/em&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="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;^/meteo&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;ROLE_USER&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "meteo" path was secured while the dashboard on the home page remained public.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Dashboard HTML template
&lt;/h2&gt;

&lt;p&gt;Everything in this Weather Station project was implemented without AI. But, as I'm not an expert in web design, &lt;strong&gt;I made an exception here and asked ChatGPT&lt;/strong&gt; to create an HTML dashboard to display the current weather data.&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%2Fgwmkudj7gqoqxq6vbam4.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%2Fgwmkudj7gqoqxq6vbam4.png" alt="Meteorological Data Dashboard" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this static HTML, I was able to create the Twig &lt;a href="https://codeberg.org/horrabin/horrameteo-web/src/branch/main/templates/base.html.twig" rel="noopener noreferrer"&gt;base template&lt;/a&gt; of the project and the &lt;a href="https://codeberg.org/horrabin/horrameteo-web/src/branch/main/templates/home/index.html.twig" rel="noopener noreferrer"&gt;dashboard page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing the API endpoint
&lt;/h2&gt;

&lt;p&gt;To connect the Weather Station with this web application, an API was required. To do this I installed the Symfony API Platform bundle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; composer require api-platform/core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The magic spell ends by adding this annotation in the MeteoData entity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="na"&gt;#[ApiResource]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What? That was all? Yes, that was all it took to have an API ready to receive data from the Weather Station. As simple as that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Securing the Access to the API
&lt;/h2&gt;

&lt;p&gt;This webapp and the weather station both run in my local home network. But I wanted to see how two different ways of authentication, user session and stateless JWT token, could coexist. Also to have some fun, of course :-)&lt;/p&gt;

&lt;p&gt;I installed this bundle to secure the API using a JWT token.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; composer require lexik/jwt-authentication-bundle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I generated the SSL keys required to create valid JWT tokens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&amp;gt;&lt;/span&gt; bin/console lexik:jwt:generate-keypair
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A manual step was required to configure the station user. As the station is a machine and not a regular user I decided to use a simple approach creating a in-memory configuration.&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;api_user_provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;                
            &lt;span class="na"&gt;users&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;station&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%env(METEO_PASSWORD)%'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ROLE_API'&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, I just needed to generate the JWT token for the station user to see if it worked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bin/console lexik:jwt:generate-token station &lt;span class="nt"&gt;--user-class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Symfony&lt;span class="se"&gt;\\&lt;/span&gt;Component&lt;span class="se"&gt;\\&lt;/span&gt;Security&lt;span class="se"&gt;\\&lt;/span&gt;Core&lt;span class="se"&gt;\\&lt;/span&gt;User&lt;span class="se"&gt;\\&lt;/span&gt;InMemoryUser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But then I got stuck... When trying to use the API it returned the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR 500: Session was used while the request was declared stateless.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I read the Symfony documentation about how to have two types of authentication in the same project, I googled it, I searched forums, I tried everything to avoid the use of AI.&lt;/p&gt;

&lt;p&gt;My &lt;em&gt;"spider sense"&lt;/em&gt; was warning me about what could be the problem, but I decided to ignore it. You know, sometimes you ignore this little voice in your head that is telling you the right thing. And then I gave in, I didn't want to, but I ended up asking the AI...&lt;/p&gt;

&lt;p&gt;What was the AI response? Exactly what my intuition was telling me :-D. It was the order of the security firewalls configuration. I had first the user authentication, that requires a session, and then the API. &lt;/p&gt;

&lt;p&gt;Changing the order and putting the API authentication first fixed the issue. Problem solved with a huge dumb face xD.&lt;/p&gt;

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

&lt;p&gt;The purpose of a framework is to allow developers to focus on the real problem, the third party libraries or bundles exist to avoid reinventing the wheel. &lt;/p&gt;

&lt;p&gt;This is not a surprise, of course, but in these times where AI seems the answer to developing fast, I wanted to prove to myself that it is possible to do it as fast as with AI. &lt;/p&gt;

&lt;p&gt;And I think that I achieved the goal, in about 16 hours of work I was able to finish the Dashboard, the admin area and the API.&lt;/p&gt;




&lt;p&gt;In the next article I will try to explain how I replicated the whole Weather Station System using AI.&lt;/p&gt;

</description>
      <category>sideprojects</category>
      <category>php</category>
      <category>symfony</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Reading Data from Weather Sensors with Python</title>
      <dc:creator>Fernando Fornieles</dc:creator>
      <pubDate>Sun, 29 Mar 2026 18:18:50 +0000</pubDate>
      <link>https://dev.to/nandofm/reading-data-from-weather-sensors-with-python-51pe</link>
      <guid>https://dev.to/nandofm/reading-data-from-weather-sensors-with-python-51pe</guid>
      <description>&lt;p&gt;Apart from building a weather station, another thing I had been wanting to do for a long time was to learn Python. With this Weather Station project I satisfied both :-)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Check the &lt;a href="https://codeberg.org/horrabin/horrameteo" rel="noopener noreferrer"&gt;source code&lt;/a&gt; of this project in &lt;a href="https://codeberg.org" rel="noopener noreferrer"&gt;Codeberg&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Learning the Pimoroni's WeatherHAT library
&lt;/h2&gt;

&lt;p&gt;The first thing I did, after having everything installed, was to learn how the Pimoroni's WeatherHAT library works in order to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read the weather data from the sensors&lt;/li&gt;
&lt;li&gt;Display the weather data in the WeatherHAT's screen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I opened an SSH connection and started playing!&lt;/p&gt;

&lt;p&gt;Apart from being my first steps with Python what I learned about the library was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is necessary to call the "&lt;em&gt;update&lt;/em&gt;" method of the sensor before getting the weather readings&lt;/li&gt;
&lt;li&gt;The first reading is "crap", it is necessary to perform an initial update and wait a few seconds before getting good reading values.&lt;/li&gt;
&lt;li&gt;There are two attributes for temperature: &lt;em&gt;device_temperature&lt;/em&gt; and &lt;em&gt;temperature&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;The temperature attribute is calculated by applying a default offset of 7.5ºC to the &lt;em&gt;device_temperature&lt;/em&gt; reading.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Calibrating the Temperature
&lt;/h2&gt;

&lt;p&gt;The first thing I focused on was to properly calibrate the temperature. For this I spent some days gathering temperature data from the sensor and from another nearby weather station at the same time, taking readings every hour.&lt;/p&gt;

&lt;p&gt;What I noticed was that the difference between both sensors wasn't a fixed value. The readings from the WeatherHAT's sensor were always between 12ºC and 16ºC higher than those from the nearby station.&lt;/p&gt;

&lt;p&gt;So instead of applying a fixed offset I tried a linear regression. I created a class to calculate the offset based on a given &lt;em&gt;device_temperature&lt;/em&gt; value.&lt;/p&gt;

&lt;p&gt;In the future I could find a better way to calculate the offset. That is why I created a &lt;em&gt;TemperatureOffsetInterface&lt;/em&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LinearRegressionOffset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TemperatureOffsetInterface&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;config&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;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&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;doLinearRegression&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;getOffset&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;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&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;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&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;a&lt;/span&gt;        
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;doLinearRegression&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;x&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;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data&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;x&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data&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;y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&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="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;       

        &lt;span class="n"&gt;medianX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;statistics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;median&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;medianY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;statistics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;median&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;numSum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;denSum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xValue&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;numSum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numSum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xValue&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;medianX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;medianY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;denSum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;denSum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;xValue&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;medianX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xValue&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;medianX&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;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numSum&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;denSum&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;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;medianY&lt;/span&gt; &lt;span class="o"&gt;-&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;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;medianX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;I could have used a Python library to perform the linear regression but, remember, the goal of this project was to learn Python ;-).&lt;/p&gt;

&lt;h2&gt;
  
  
  Decoupling From The Hardware
&lt;/h2&gt;

&lt;p&gt;Having solved the calibration of the temperature sensor it was time to start coding the reading system.&lt;/p&gt;

&lt;p&gt;The process should run periodically, for example every 5 minutes. On each iteration the system should read the data from the sensor, apply the temperature offset, display the data on the screen and send it to an external service.&lt;/p&gt;

&lt;p&gt;I work on my own PC, as it is more comfortable than coding directly in the Pi. That is why I created some mocks to simulate access to the WeatherHAT hardware.&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%2Fq0zbczi5icf7f4m1dplm.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%2Fq0zbczi5icf7f4m1dplm.png" alt="The interfaces and implementations to mock the access to the WeatherHAT hardware" width="692" height="142"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This way I could code the main algorithm decoupled from the hardware.&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="n"&gt;sensor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSensor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createDisplay&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the main algorithm was done I simply had to code the hardware-specific parts. I had to upload each change to the Pi but using the &lt;a href="https://github.com/pimoroni/weatherhat-python/tree/main/examples" rel="noopener noreferrer"&gt;examples provided by Pimoroni&lt;/a&gt; it was more or less easy to get the data from the sensor and show it in the WeatherHAT screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending The Data To An External Service
&lt;/h2&gt;

&lt;p&gt;I wanted to see the current temperature from anywhere and also store the readings in order to generate statistic from the weather data. &lt;/p&gt;

&lt;p&gt;So the next step was to send the data to an external service. As I hadn't yet implemented my Weather Data Webapp, I started sending the data to Adafruit. I created a dashboard to see current data and also a graph to easily compare the temperature readings with the nearby station. This way I could check the quality of the linear regression.&lt;/p&gt;

&lt;p&gt;I created an &lt;em&gt;ConnectorInterface&lt;/em&gt; to first create the Adafruit connector and later the implementation to send the weather data to my Weather Data Webapp.&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConnectorInterface&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&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;WeatherData&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also created a &lt;em&gt;MockConnector&lt;/em&gt; to avoid sending bad data while working on the main flow of the algorithm.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing An Abstract Factory
&lt;/h2&gt;

&lt;p&gt;Everything was working fine, but changing which implementations to use while testing the system locally (mock) and in "production" (on the Pi) was a bit messy.&lt;/p&gt;

&lt;p&gt;To simplify the mode selection I applied the Abstract Factory Pattern. This way, with a single configuration setting, I could load the right set of components (Sensor, Display and Connector).&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SystemFactoryInterface&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;createDisplay&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DisplayInterface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;createConnector&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;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ConnectorInterface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;createSensor&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;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;SensorInterface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;     
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;I enjoyed this part a lot. Learn Python, read the data from the sensor, calibrate the temperature, apply some design patterns, ... It brought back the feeling I had when I first started learning to program. &lt;/p&gt;

&lt;p&gt;Maybe because it is a project that is really different to the ones I'm more used to develop, maybe because it mixes different things or maybe both :-).&lt;/p&gt;

&lt;p&gt;In the next article I'll try to explain the implementation of the Weather Data Webapp. This time with PHP and Symfony, a language and a  framework that I'm more used to work with.&lt;/p&gt;

</description>
      <category>python</category>
      <category>sideprojects</category>
      <category>learning</category>
      <category>community</category>
    </item>
    <item>
      <title>Building a Weather Station Using an Old Raspberry Pi</title>
      <dc:creator>Fernando Fornieles</dc:creator>
      <pubDate>Mon, 23 Mar 2026 17:20:41 +0000</pubDate>
      <link>https://dev.to/nandofm/building-a-weather-station-using-an-old-raspberry-pi-5333</link>
      <guid>https://dev.to/nandofm/building-a-weather-station-using-an-old-raspberry-pi-5333</guid>
      <description>&lt;p&gt;For a long time I wanted to build a weather station at home because I like meteorology and for the fun of doing it. So I decided to stop waiting and just do it :-)&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting the sensors
&lt;/h2&gt;

&lt;p&gt;I wanted something easy to attach to my old Pi, a Raspberry Pi 2 model B+, that would let me easily read the data from the sensors. The Pimoron's Weather HAT fit perfectly with what I wanted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Just connect it to the GPIO interface&lt;/li&gt;
&lt;li&gt;Download and install the Python library provided by Pimoroni&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://learn.pimoroni.com/article/getting-started-with-weather-hat" rel="noopener noreferrer"&gt;Pimoroni has a good tutorial&lt;/a&gt; that helps you build it and solve some of the common issues. There are also &lt;a href="https://www.raspberrypi.com/news/build-a-weather-station-with-a-web-dashboard-hackspace-58/" rel="noopener noreferrer"&gt;other articles from the community&lt;/a&gt; that are really helpful.&lt;/p&gt;

&lt;p&gt;One good thing about this Weather HAT is that it is possible to connect a wind and a rain gauge. I don't have so much space in my balcony so I didn't get these sensors, but it is something that I would like to have in the future.&lt;/p&gt;

&lt;p&gt;There is a good set of examples to read the data from the sensors and display it available in the &lt;a href="https://github.com/pimoroni/weatherhat-python/tree/main/examples" rel="noopener noreferrer"&gt;Weather HAT's GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenges
&lt;/h2&gt;

&lt;p&gt;But it has an important downside, the sensor temperature is affected by the Pi's CPU as it is very close to the board. So the temperature needs to be compensated, which is not as simple as applying a fixed offset as stated in the Pimoroni's tutorial. Instead I tried to apply a linear regression by obtaining the temperature from another weather station that, luckily, is close to my home. &lt;/p&gt;

&lt;p&gt;Apart of installing the Weather HAT and reading the data from the sensors, the most important challenge was finding the best place for it in the balcony and how to isolate it from the external conditions, while at the same time allowing enough ventilation to avoid heat accumulation inside.&lt;/p&gt;

&lt;h2&gt;
  
  
  Protecting the Weather Station from the Environment
&lt;/h2&gt;

&lt;p&gt;Once I had the Weather HAT installed I used the following materials to place the station on the balcony:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A transparent plastic lunchbox&lt;/li&gt;
&lt;li&gt;A base enclosure for the Pi&lt;/li&gt;
&lt;li&gt;Some adhesive protectors for furniture&lt;/li&gt;
&lt;li&gt;Some glue&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Maybe a lunchbox is not the best solution but using a Stevenson Screen could be a bit overkill.&lt;/p&gt;

&lt;p&gt;I used the furniture protectors to separate a little bit the Pi from the back of the lunchbox, this way the sensor has some additional separation from the wall. &lt;/p&gt;

&lt;p&gt;At the same time I made some holes in the lunchbox to let the air flow inside to cool the Pi. I made some of the holes on the side where the sensors are located and the others on a lateral.&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%2Fb2lmm72evr2hm5tcgd8a.jpg" 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%2Fb2lmm72evr2hm5tcgd8a.jpg" alt="The holes in the lunchbox where the weather station is" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A few days later I noticed that, due to that holes, on windy days the measure of the temperature sensor can provide values a little bit lower than the real one. But I prefer this to having the Pi without this air cooling. I don't have the perfect materials, so I can't expect to have the perfect measures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Calibrating The Temperature Sensor
&lt;/h2&gt;

&lt;p&gt;As I mentioned above the readings of the temperature sensor need to be compensated as it is close to the Pi's CPU.&lt;/p&gt;

&lt;p&gt;The Pimoroni Python library for the Weather HAT has an attribute to set an offset that is applied to the device temperature measure. So I tried comparing the Weather HAT sensor values with the temperature provided by a nearby weather station.&lt;/p&gt;

&lt;p&gt;What I noticed was that the offset to apply can't be a fixed value. The difference between the real temperature and the values provided by the Weather HAT was always between 12º and 16º. &lt;/p&gt;

&lt;p&gt;To solve this I create a small script to get temperature data from both stations at the same time. With this series of data I was able to create a linear regresssion model to calculate the offset that should be applied to the HAT's device temperature reading.&lt;/p&gt;

&lt;p&gt;It is not still perfect. When the device temperature is close to 30º the difference is quite high, but for the moment, it seems a little bit better than applying a fixed offset as you can see 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%2Ffqdbugre7jgieubdee10.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%2Ffqdbugre7jgieubdee10.png" alt="A line graph showing the device temperature, the actual temperature and the calculated temperature applying the offset obtained from the linear regression" width="491" height="299"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the next article I'll describe my Python learnings implementing the software to gather the measures, display it and sending it to an external system.&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>sideprojects</category>
      <category>learning</category>
      <category>community</category>
    </item>
    <item>
      <title>A Home Weather Monitoring System Built Without AI</title>
      <dc:creator>Fernando Fornieles</dc:creator>
      <pubDate>Thu, 19 Mar 2026 22:56:25 +0000</pubDate>
      <link>https://dev.to/nandofm/a-home-weather-monitoring-system-built-without-ai-4a29</link>
      <guid>https://dev.to/nandofm/a-home-weather-monitoring-system-built-without-ai-4a29</guid>
      <description>&lt;p&gt;Coding without using AI may seem revolutionary nowadays but what I wanted was simply to learn Python and enjoy the act of coding with my own hands.&lt;/p&gt;

&lt;p&gt;So I built a Home Weather Monitoring System with the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A weather station&lt;/li&gt;
&lt;li&gt;A software to read the sensor &lt;del&gt;lectures&lt;/del&gt; measures&lt;/li&gt;
&lt;li&gt;A web application to view the Weather Data&lt;/li&gt;
&lt;/ul&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%2F8j73zzkmi6o1qiau0xnn.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%2F8j73zzkmi6o1qiau0xnn.png" alt="A diagram describing the components of the weather system: a weather station, a sensor reader and a web application" width="492" height="134"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Weather Station
&lt;/h2&gt;

&lt;p&gt;I wanted something easy to build, so I recycled an old Raspberry Pi to attach a Pimoroni's Weather HAT to it. This board has sensors to measure temperature, pressure, humidity and luminosity and a little screen to display the weather data.&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%2F4fubni4r8lp87c781xl3.jpg" 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%2F4fubni4r8lp87c781xl3.jpg" alt="A photo of the weather station installed on the Pi" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For reading the sensors and display the info on the screen, Pimoroni provides a Python library and some examples of how to use it. It is really easy and a good starting point to experiment.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Sensor Reader Software
&lt;/h2&gt;

&lt;p&gt;Once the Weather HAT is installed and having played with the Python library, it's time to write the software to do what you need.&lt;/p&gt;

&lt;p&gt;In my case I wanted to display the reading on the screen to see them directly. The other thing I wanted was to send the weather data to a web application in order to check the current values from anywhere and keep a history of the data.&lt;/p&gt;

&lt;p&gt;But since it is more comfortable for me to code on my PC instead of doing it directly on the Pi, I had to create a mock for the sensor reader and the screen.&lt;/p&gt;

&lt;p&gt;This was a good opportunity to achieve my goal of learning Python more in depth by creating interfaces and applying design patterns :-)&lt;/p&gt;

&lt;h2&gt;
  
  
  The Weather Data Web Application
&lt;/h2&gt;

&lt;p&gt;As mentioned above, I wanted a web application to store the weather history and show the current weather values from anywhere.&lt;/p&gt;

&lt;p&gt;To build this I choose PHP and Symfony as a framework. As I have experience with both, the challenge was to see how fast I can implement this web application using only the productivity tools provided by the framework and its bundles.&lt;/p&gt;

&lt;p&gt;The web application has the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An API endpoint to receive the weather data, secured by a JWT token&lt;/li&gt;
&lt;li&gt;A front page to show the current data as well as some statistics and evolution graphs&lt;/li&gt;
&lt;li&gt;A private backoffice to manage the weather data, secured by a username and password.&lt;/li&gt;
&lt;/ul&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%2F4hyn2o3hds8buqjvqij9.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%2F4hyn2o3hds8buqjvqij9.png" alt="A diagram showing the component of the weather data web application" width="572" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the first article of a series where I will share, for those that may find this interesting, my learnings and how I enjoy building my personal Home Weather Monitoring System. &lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>sideprojects</category>
      <category>community</category>
      <category>learning</category>
    </item>
    <item>
      <title>Interesting article I've read today about the repeated promise of eliminating programmers in the history of software development.
https://www.ivanturkovic.com/2026/01/22/history-software-simplification-cobol-ai-hype/</title>
      <dc:creator>Fernando Fornieles</dc:creator>
      <pubDate>Tue, 03 Mar 2026 15:21:32 +0000</pubDate>
      <link>https://dev.to/nandofm/interesting-article-ive-read-today-about-the-repeated-promise-of-eliminating-programmers-in-the-583f</link>
      <guid>https://dev.to/nandofm/interesting-article-ive-read-today-about-the-repeated-promise-of-eliminating-programmers-in-the-583f</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://www.ivanturkovic.com/2026/01/22/history-software-simplification-cobol-ai-hype/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic.packt-cdn.com%2Fproducts%2F9781785284687%2Fcover%2Fsmaller" height="312" class="m-0" width="250"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://www.ivanturkovic.com/2026/01/22/history-software-simplification-cobol-ai-hype/" rel="noopener noreferrer" class="c-link"&gt;
            The Eternal Promise: A History of Attempts to Eliminate Programmers
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            From COBOL in the 1960s to AI in the 2020s, every generation promises to eliminate programmers. Explore the recurring cycles of software simplification hype.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
          ivanturkovic.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Embrace Software Entropy, you can't avoid it</title>
      <dc:creator>Fernando Fornieles</dc:creator>
      <pubDate>Mon, 09 Feb 2026 07:00:00 +0000</pubDate>
      <link>https://dev.to/nandofm/embrace-software-entropy-you-cant-avoid-it-io7</link>
      <guid>https://dev.to/nandofm/embrace-software-entropy-you-cant-avoid-it-io7</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The wise programmer is told about Tao and follows it. The average programmer is told about Tao and searches for it. The foolish programmer is told about Tao and laughs at it. If it were not for laughter, there would be no Tao.&lt;br&gt;
The highest sounds are hardest to hear. Going forward is a way to retreat. Great talent shows itself late in life. Even a perfect program still has bugs.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;— Geoffrey James (&lt;a href="http://www.mit.edu/~xela/tao.html" rel="noopener noreferrer"&gt;The Tao of Programming&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At the beginning of any project there are a lot of decisions to be made. What is the best approach to solve the problem? What is the architecture style that fits best? Is the design of the solution accurate? Are we missing something? &lt;/p&gt;

&lt;p&gt;Even before starting to code there are a lot of things to think about. Should I create an interface or an abstract class? This design pattern really fits here? How can I approach this corner case?&lt;/p&gt;

&lt;p&gt;This usually tends us to fall into the &lt;a href="https://en.wikipedia.org/wiki/Analysis_paralysis" rel="noopener noreferrer"&gt;Analysis Paralysis&lt;/a&gt; trap. We want to take perfect decisions, to design the most suitable solution for the problem, to write the best maintainable code, ... But there is no a perfect decision, no perfect technology, no perfect code, ... Because from that initial moment, entropy will only increase throughout the whole life of the project. This fact will make you question the decisions made in the past.&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%2Fz56kcj2e31kjcdngaxr9.jpeg" 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%2Fz56kcj2e31kjcdngaxr9.jpeg" alt="A plaque on a wall that says: " width="588" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Software entropy&lt;/strong&gt;, the level of chaos in any software system, is not only about the &lt;strong&gt;accidental complexity&lt;/strong&gt; you may add in form of technical debt, lack of understanding or experience, over-engineering or whatever other reason. It is also about the &lt;strong&gt;essential complexity&lt;/strong&gt; of the domain and its requirements which introduces its own level of entropy.&lt;/p&gt;

&lt;p&gt;You may write the best code, take the best technological decisions, have 0% of technical debt, ... but the software entropy of your project will get higher and higher any time you implement a new feature, solve a bug or make an "innocent" change in a single line. &lt;strong&gt;All of these may increase entropy even being perfectly implemented&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Software entropy is unavoidable. The more changes we introduce the more entropy we create, the faster we introduce changes the faster the entropy grows. It can be reduced, or at least mitigated, by refactoring the code, applying best practices and trying to &lt;a href="https://dev.to/nandofm/avoiding-complexity-in-software-development-5992"&gt;avoid accidental complexity&lt;/a&gt;. But the long-term trend for entropy will be to increase.&lt;/p&gt;

&lt;p&gt;Taking time to consider different options is the way to ensure that a solution is a good one, maybe not the best but a good one. Just stop the Analysis Paralysis, take a decision and after that flow with the entropy. :-)&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>softwaredevelopment</category>
      <category>discuss</category>
      <category>community</category>
    </item>
    <item>
      <title>Interesting article about our ego as developers I read the other day.
https://shiftmag.dev/developers-your-ego-is-the-real-bug-in-the-system-7657/</title>
      <dc:creator>Fernando Fornieles</dc:creator>
      <pubDate>Wed, 04 Feb 2026 11:31:18 +0000</pubDate>
      <link>https://dev.to/nandofm/interesting-article-about-our-ego-as-developers-i-read-the-other-day-e87</link>
      <guid>https://dev.to/nandofm/interesting-article-about-our-ego-as-developers-i-read-the-other-day-e87</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://shiftmag.dev/developers-your-ego-is-the-real-bug-in-the-system-7657/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshiftmag.dev%2Fwp-content%2Fuploads%2F2026%2F01%2Fivan-kranjec-2.png" height="420" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://shiftmag.dev/developers-your-ego-is-the-real-bug-in-the-system-7657/" rel="noopener noreferrer" class="c-link"&gt;
            What is egoless programming?
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            How ego-driven decisions cause software teams to fail, and how egoless development builds trust, better collaboration, and stronger products.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fshiftmag.dev%2Fwp-content%2Fuploads%2F2024%2F08%2Fcropped-ShiftMag-favicon-32x32.png%3Fx73249" width="32" height="32"&gt;
          shiftmag.dev
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Once upon a time when I built AI agents…: A grandpa story</title>
      <dc:creator>Fernando Fornieles</dc:creator>
      <pubDate>Tue, 20 Jan 2026 13:30:00 +0000</pubDate>
      <link>https://dev.to/nandofm/once-upon-a-time-when-i-built-ai-agents-a-grandpa-story-1e71</link>
      <guid>https://dev.to/nandofm/once-upon-a-time-when-i-built-ai-agents-a-grandpa-story-1e71</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Most of the time, when searching for information about anything on Internet, you get lots of results that are mostly not exactly what you were looking for. A search through these results is needed until you find what you really need, wasting time that could be dedicated to something else. The purpose of the project is to build a mechanism that applying Artificial Intelligence techniques, would create agents capable of showing only relevant information to the user.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is an excerpt from my final degree project, &lt;em&gt;“Content Personalization and Subjectivity Capture using Case-Based Reasoning and Fuzzy Logic techniques”&lt;/em&gt;, presented in 2001 at the University of Girona.&lt;/p&gt;

&lt;p&gt;The goal was to build a system able to customize content, based on user preferences, trying to catch their subjectivity. That means not only capturing what things a person may like but also in which degree.&lt;/p&gt;

&lt;p&gt;Agents Inspired, a spin-off company founded by my project supervisor and other teachers, had the same goal. Having the opportunity to work on something really close to my degree project was something that I couldn’t pass up. So when my supervisor told me if I would like to join I had no doubt!&lt;/p&gt;

&lt;p&gt;During this intense period, I learned an important thing about AI: it is as “simple” as applying maths to a set of data. A system with a well categorized set of data and a good mathematical model should be able to provide personalized content to the user.&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%2Flr1nasdsvhrv47m1ativ.webp" 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%2Flr1nasdsvhrv47m1ativ.webp" alt="One of the formulas to calculate the distance between profiles that was in my final degree project. Please, don’t make me explain it, I lost my memory about it!" width="720" height="134"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;One of the formulas to calculate the distance between profiles that was in my final degree project. Please, don’t make me explain it, I lost my memory about it!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But this is just one part of the story. The real challenge in any AI system is the processing power needed to be able to perform these calculations. This problem can be mitigated by applying better algorithms and fine-tuning the databases, as we tried in our old AI system, but at a large scale this is not enough.&lt;/p&gt;

&lt;p&gt;The growing number of AI Data Centers around the world, which are causing a huge environmental impact, will probably set the limit of what we can achieve with AI. It doesn’t matter how good the mathematical model is or how good the quality of the dataset can be. AI power requirements will push it to the limit of scaling.&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%2F5i0tbcedg45nk0bqnu0t.webp" 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%2F5i0tbcedg45nk0bqnu0t.webp" alt="A screenshot of Dr. Sbaitso (1992), one of the ancestors of ChatGPT" width="652" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;a href="https://en.wikipedia.org/wiki/Dr._Sbaitso" rel="noopener noreferrer"&gt;Dr. Sbaitso&lt;/a&gt; (1992), one of the ancestors of ChatGPT&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some years later, after leaving the company, I read about the RSS format and I immediatly saw the potential to apply AI to create an agent that, reading information from several feeds and learning from my interactions, could create some sort of a personalized “newspaper” by leveraging the categorization feature of RSS and analyzing the words from each entry.&lt;/p&gt;

&lt;p&gt;So I built a Java Swing application that worked nicely. But only at the beginning… I quickly realize that the more things the system learned, the less it knew about me. This was due because, like anyone else, I have several interests and as the system widened its knowledge about me the distribution of probabilities became so similar that it couldn’t tell what was actually interesting.&lt;/p&gt;

&lt;p&gt;This was another important lesson learned about AI and data: more data doesn’t necessarily mean better results. But there is another important issue, AI as a recommendation system is limited by what it knows about you. What about those things that are far from your taste profile but you may like? How can you broaden your musical taste, for example, if the system never recommends you music genres that you have never listened to?&lt;/p&gt;

&lt;p&gt;Apart from this, during my professional career and also for personal interest, I took courses and trainings about AI, Machine Learning, Natural Language Processing, …&lt;/p&gt;

&lt;p&gt;This personal experience doesn’t make me an AI expert, that is far beyond my capabilities, but I hope it provides me with enough judgement to avoid the hype around it. My genuine interest is because I truly believe that we can find use cases where this amazing tool can be useful but not in the way that is currently presented.&lt;/p&gt;

&lt;p&gt;Current AI is just a “wordjoiner”, it can’t reason or understand anything, it just joins words that look good together. So that is why It is hard for me to understand why people blindly trust in this systems when the result is totally impredictible.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Cover Image is from the film War Games (1983) where an AI called Joshua could provoke a global thermonuclear war&lt;/em&gt;&lt;/p&gt;

</description>
      <category>story</category>
      <category>ai</category>
      <category>discuss</category>
    </item>
    <item>
      <title>AI: The Danger of Endogamic Programming</title>
      <dc:creator>Fernando Fornieles</dc:creator>
      <pubDate>Mon, 19 Jan 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/nandofm/ai-the-danger-of-endogamic-programming-3717</link>
      <guid>https://dev.to/nandofm/ai-the-danger-of-endogamic-programming-3717</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;There once was a Master Programmer who wrote unstructured programs. A novice programmer, seeking to imitate him, also began to write unstructured programs. When the novice asked the Master to evaluate his progress, the Master criticized him for writing unstructured programs, saying, “What is appropriate for the Master is not appropriate for the novice. You must understand Tao before transcending structure.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;— &lt;em&gt;Geoffrey James (&lt;a href="http://www.mit.edu/~xela/tao.html" rel="noopener noreferrer"&gt;The Tao of Programming&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Learning to program in any language is relatively easy. What is not easy is developing analytical skills to structure it properly. The solution, however, is simple: face new problems, write more code, fail, and apply what you learn to solve even more problems.&lt;/p&gt;

&lt;p&gt;As developers gain experience, they spend more time thinking about how to solve a problem than writing code. This natural progression leads them closer to the Tao of Programming ;-), giving them the judgment needed to choose the right techniques and tools. AI is one such tool, powerful and useful when used wisely. But to use AI wisely, one must be close to the Tao.&lt;/p&gt;

&lt;p&gt;If a novice developer relies blindly on AI instead of making the effort to find solutions themselves, they risk slowing their growth. In the end, they might have working code, but without understanding why it works. This lack of understanding can lead to massive technical debt.&lt;/p&gt;

&lt;p&gt;However, the risks go beyond technical debt. If AI is used to generate most of the code without being fed with new ideas or creative solutions, what happens to innovation? If creativity, the force that arises from trying to solve problems, disappears, then how can AI generate solutions for challenges that don’t yet exist?&lt;/p&gt;

&lt;p&gt;Even worse, if AI is trained using its own generated solutions, we risk entering a new paradigm: &lt;strong&gt;Endogamic Programming&lt;/strong&gt;, a stagnant cycle where the knowledge base is fed only by its own outputs. Like stagnant water, this would lead to decay, limiting true progress.&lt;/p&gt;

&lt;p&gt;Maybe I’m exaggerating, of course, but if we abandon the path of the Tao, what could go wrong?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;PS: I have written and re-written this article several times until I felt it was what I wanted to express. I have use AI only to help me on the grammar and the clarity of the text. And I think that this is a legitimate use of this tool :-)&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>softwareengineering</category>
      <category>programming</category>
      <category>vibecoding</category>
    </item>
    <item>
      <title>Avoiding complexity in software development</title>
      <dc:creator>Fernando Fornieles</dc:creator>
      <pubDate>Thu, 15 Jan 2026 08:21:23 +0000</pubDate>
      <link>https://dev.to/nandofm/avoiding-complexity-in-software-development-5992</link>
      <guid>https://dev.to/nandofm/avoiding-complexity-in-software-development-5992</guid>
      <description>&lt;p&gt;Keeping things simple is challenging. It’s easy to fall in over-engineering, implementing bright technical features just because we think they will be needed in the future or falling in the latest and coolest hype, applying it in our system only to realize later that it wasn’t the best solution for our problem.&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%2Fn64jym1t8ltx9is9kvzd.webp" 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%2Fn64jym1t8ltx9is9kvzd.webp" alt="a joke about YAGNI principle showing an airplane motor with a horse chair on" width="800" height="748"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“People often optimize for the problems they want and not necessarily for the problems they have.” &lt;br&gt;
— Lars Rosenquist &lt;a href="https://redis.com/blog/5-microservices-misconceptions/" rel="noopener noreferrer"&gt;https://redis.com/blog/5-microservices-misconceptions/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No matter our level of experience, adding accidental complexity to a system is all too common. To mitigate it, we must exercise caution, share ideas, learn from colleagues, and adhere to principles that prioritize simplicity.&lt;/p&gt;

&lt;p&gt;Fortunately, we benefit from the wisdom of those who came before us, and we can build a good set of principles based on their guidance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delay decisions whenever possible
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“A good architecture maximizes the number of decisions NOT made.”&lt;br&gt;
— Robert C. Martin (Clean Architecture)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I take this principe as a good advise to focus on relevant problems at each stage of a project rather than solving everything at once.&lt;/p&gt;

&lt;p&gt;A good strategy to incrementally build a system can help us delay some decisions, be focused on the current problems and to reduce the cognitive load as well, which is one of the main causes that adds accidental complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid premature optimization
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Be wary of premature optimization. It’s always a good idea to make sure an algorithm really is a bottleneck before investing your precious time trying to improve it.”&lt;br&gt;
— Andrew Hunt and David Thomas (The Pragmatic Programmer)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Writing performant code that sacrifices readability without clear evidence of performance issues is not only wasteful but also accumulates technical debt in the form of poor maintainability.&lt;/p&gt;

&lt;p&gt;Improving the performance of clean code is always easier than making performant code readable. Optimize your code only when it is necessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow the YAGNI principle
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Always implement things when you actually need them, never when you just foresee that you need them.”&lt;br&gt;
— Ron Jeffries&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;YAGNI, You Ain’t Gonna Need It, as simple as that.&lt;/p&gt;

&lt;p&gt;Implementing certain features just because we think they will be needed in the future and will save us work is not a valid reason to do so. Think about the consequences. What if the anticipated features, implemented ‘just in case’ are never requested or prove unnecessary? We risk ending up with unnecessary code. Worse yet, what if the requested features are the opposite to what we expect? This would necessitate a complete refactor.&lt;/p&gt;

&lt;p&gt;Following the YAGNI principle ensures we avoid unnecessary complexity, making the system more adaptable to evolving requirements.&lt;/p&gt;




&lt;p&gt;There are many other useful principles that could be added, but doing so might introduce unnecessary complexity to this set ;-)&lt;/p&gt;

&lt;p&gt;As someone wise once said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Those are my principles, and if you don’t like them…well, I have others.”&lt;br&gt;
— Groucho Marx&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>architecture</category>
      <category>development</category>
      <category>softwareengineering</category>
      <category>bestpractice</category>
    </item>
  </channel>
</rss>
