<?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: Maksim Pugin</title>
    <description>The latest articles on DEV Community by Maksim Pugin (@maksimpugin).</description>
    <link>https://dev.to/maksimpugin</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4195684%2F35e48b8a-796e-4f86-b582-d85dc54656ad.jpg</url>
      <title>DEV Community: Maksim Pugin</title>
      <link>https://dev.to/maksimpugin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/maksimpugin"/>
    <language>en</language>
    <item>
      <title>Why Your Python Script Works Locally but Fails in Production</title>
      <dc:creator>Maksim Pugin</dc:creator>
      <pubDate>Mon, 02 Nov 2026 06:54:43 +0000</pubDate>
      <link>https://dev.to/maksimpugin/why-your-python-script-works-locally-but-fails-in-production-22ck</link>
      <guid>https://dev.to/maksimpugin/why-your-python-script-works-locally-but-fails-in-production-22ck</guid>
      <description>&lt;p&gt;&lt;em&gt;Your code works perfectly on your machine. Then you deploy it, and everything starts breaking. Sound familiar?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There's a moment almost every Python developer experiences.&lt;/p&gt;

&lt;p&gt;You've spent hours building a script. You've tested it several times. Everything works exactly as expected.&lt;/p&gt;

&lt;p&gt;Then you move it to a server.&lt;/p&gt;

&lt;p&gt;And suddenly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The API stops responding.&lt;/li&gt;
&lt;li&gt;A file can't be found.&lt;/li&gt;
&lt;li&gt;A library behaves differently.&lt;/li&gt;
&lt;li&gt;The application crashes without a useful error message.&lt;/li&gt;
&lt;li&gt;A process that previously took 30 seconds now runs indefinitely.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The frustrating part?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You haven't changed the code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So what went wrong?&lt;/p&gt;

&lt;p&gt;The answer is usually simple: your local environment and your production environment are not the same.&lt;/p&gt;

&lt;p&gt;And writing code that works is only the first step toward building software that works reliably.&lt;/p&gt;

&lt;p&gt;Let's explore seven common problems that turn working Python scripts into production failures — and how to prevent them.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Hidden Danger of Missing Timeouts
&lt;/h2&gt;

&lt;p&gt;Let's start with something that looks completely harmless.&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.example.com/products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code works perfectly when the API is available and responds quickly.&lt;/p&gt;

&lt;p&gt;But what happens when the server stops responding?&lt;/p&gt;

&lt;p&gt;By default, the &lt;code&gt;requests&lt;/code&gt; library doesn't set a timeout.&lt;/p&gt;

&lt;p&gt;That means your application could wait indefinitely for a response.&lt;/p&gt;

&lt;p&gt;Now imagine this script is running as part of a scheduled job.&lt;/p&gt;

&lt;p&gt;One unavailable API could block the entire workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  A better approach
&lt;/h3&gt;



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

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_products&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.example.com/products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Request timed out&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestException&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Request failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Invalid JSON response&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the timeout configuration specifies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3 seconds for establishing the connection.&lt;/li&gt;
&lt;li&gt;10 seconds for waiting between socket read operations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One important detail: these timeouts are not a strict 13-second limit on the total request duration.&lt;/p&gt;

&lt;p&gt;A server that continuously sends small amounts of data may keep the connection active much longer.&lt;/p&gt;

&lt;p&gt;For strict execution deadlines, you need additional controls at the application or infrastructure level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; External services are not guaranteed to respond, even when your code is correct.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Hardcoded Paths Work Until They Don't
&lt;/h2&gt;

&lt;p&gt;Consider a script that saves data to a CSV file.&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;C:/Users/Developer/Desktop/products.csv&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;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writerow&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&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;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This might work perfectly on your Windows laptop.&lt;/p&gt;

&lt;p&gt;But what happens when you deploy it to a Linux server?&lt;/p&gt;

&lt;p&gt;The directory doesn't exist.&lt;/p&gt;

&lt;p&gt;The application fails.&lt;/p&gt;

&lt;p&gt;This problem becomes even more common when working with Docker containers, virtual machines, or cloud environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use pathlib instead
&lt;/h3&gt;

&lt;p&gt;Python provides a much better way to handle filesystem paths.&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;

&lt;span class="n"&gt;BASE_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;

&lt;span class="n"&gt;DATA_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BASE_DIR&lt;/span&gt; &lt;span class="o"&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="n"&gt;DATA_DIR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;OUTPUT_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DATA_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;products.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;OUTPUT_FILE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&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;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writerow&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&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;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach is more portable and avoids operating-system-specific path separators.&lt;/p&gt;

&lt;p&gt;However, there's another important production consideration.&lt;/p&gt;

&lt;p&gt;The application directory might be read-only.&lt;/p&gt;

&lt;p&gt;In containerized environments, you may also lose local files when a container is replaced.&lt;/p&gt;

&lt;p&gt;For persistent data, configure an appropriate storage location or use an external storage service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; Never assume that the production filesystem looks like your development machine.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Your Dependencies Are Part of Your Application
&lt;/h2&gt;

&lt;p&gt;Here's a situation that can be surprisingly difficult to debug.&lt;/p&gt;

&lt;p&gt;You develop a Python application using several libraries.&lt;/p&gt;

&lt;p&gt;Everything works.&lt;/p&gt;

&lt;p&gt;A colleague clones the repository and runs the same code.&lt;/p&gt;

&lt;p&gt;It crashes.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because your environments contain different dependency versions.&lt;/p&gt;

&lt;p&gt;For example, your application might rely on behavior that changed between major versions of a library.&lt;/p&gt;

&lt;h3&gt;
  
  
  The wrong approach
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;requests beautifulsoup4 pandas
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This installs available versions that satisfy the requirements at installation time.&lt;/p&gt;

&lt;p&gt;But those versions may differ across machines or deployments.&lt;/p&gt;

&lt;h3&gt;
  
  
  A more reliable approach
&lt;/h3&gt;

&lt;p&gt;For a simple project, you can maintain a dependency file with pinned versions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;requests==2.32.3
beautifulsoup4==4.12.3
pandas==2.2.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are example versions, not necessarily the versions your project should use.&lt;/p&gt;

&lt;p&gt;Then install dependencies with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For larger applications, consider tools such as &lt;code&gt;uv&lt;/code&gt;, Poetry, or pip-tools to generate and maintain lockfiles.&lt;/p&gt;

&lt;p&gt;Also remember that dependency versions aren't the entire environment.&lt;/p&gt;

&lt;p&gt;Python version, operating system, native libraries, and system packages can affect application behavior.&lt;/p&gt;

&lt;p&gt;For example, a package with native extensions might work on one platform but fail on another.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; Reproducibility is a requirement, not a convenience.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Print Statements Are Not a Logging Strategy
&lt;/h2&gt;

&lt;p&gt;During development, many of us debug code like this:&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Starting parser&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Opening page&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Extracting products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Done&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's nothing inherently wrong with &lt;code&gt;print()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For small scripts, it's often sufficient.&lt;/p&gt;

&lt;p&gt;But imagine your application runs every 15 minutes on a remote server.&lt;/p&gt;

&lt;p&gt;One execution fails at 3:00 AM.&lt;/p&gt;

&lt;p&gt;You need to know:&lt;/p&gt;

&lt;p&gt;When did the failure occur?&lt;/p&gt;

&lt;p&gt;Which operation failed?&lt;/p&gt;

&lt;p&gt;What exception was raised?&lt;/p&gt;

&lt;p&gt;How frequently has this happened?&lt;/p&gt;

&lt;p&gt;Was the failure temporary or persistent?&lt;/p&gt;

&lt;p&gt;Random print statements quickly become difficult to manage.&lt;/p&gt;

&lt;p&gt;Use Python's logging module&lt;/p&gt;

&lt;p&gt;import logging&lt;/p&gt;

&lt;p&gt;logging.basicConfig(&lt;br&gt;
    level=logging.INFO,&lt;br&gt;
    format=(&lt;br&gt;
        "%(asctime)s | "&lt;br&gt;
        "%(levelname)s | "&lt;br&gt;
        "%(name)s | "&lt;br&gt;
        "%(message)s"&lt;br&gt;
    )&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;logger = logging.getLogger(&lt;strong&gt;name&lt;/strong&gt;)&lt;/p&gt;

&lt;p&gt;def process_products():&lt;br&gt;
    logger.info("Starting product processing")&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;try:
    result = 10 / 0
    return result

except ZeroDivisionError:
    logger.exception(
        "Product processing failed"
    )
    raise
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The logger.exception() method automatically includes traceback information when called inside an exception handler.&lt;/p&gt;

&lt;p&gt;That makes debugging significantly easier.&lt;/p&gt;

&lt;p&gt;For production systems, structured JSON logs can make filtering and analyzing events even more effective.&lt;/p&gt;

&lt;p&gt;One important rule: never log passwords, access tokens, or other sensitive information.&lt;/p&gt;

&lt;p&gt;Lesson: If your application fails and you can't explain why, you have an observability problem.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Environment Variables Are Better Than Hardcoded Secrets&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Imagine finding this in a repository:&lt;/p&gt;

&lt;p&gt;DATABASE_HOST = "192.168.1.100"&lt;br&gt;
DATABASE_USER = "admin"&lt;br&gt;
DATABASE_PASSWORD = "super_secret_password"&lt;/p&gt;

&lt;p&gt;There are two problems.&lt;/p&gt;

&lt;p&gt;First, these settings might only work in one environment.&lt;/p&gt;

&lt;p&gt;Second, credentials embedded in source code create unnecessary security risks.&lt;/p&gt;

&lt;p&gt;Separate configuration from code&lt;/p&gt;

&lt;p&gt;import os&lt;/p&gt;

&lt;p&gt;DATABASE_HOST = os.environ.get(&lt;br&gt;
    "DATABASE_HOST",&lt;br&gt;
    "localhost"&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;DATABASE_USER = os.environ["DATABASE_USER"]&lt;/p&gt;

&lt;p&gt;DATABASE_PASSWORD = os.environ[&lt;br&gt;
    "DATABASE_PASSWORD"&lt;br&gt;
]&lt;/p&gt;

&lt;p&gt;Now the application can use different settings in development, testing, and production without modifying its source code.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;export DATABASE_HOST="db.internal"&lt;br&gt;
export DATABASE_USER="app_user"&lt;br&gt;
export DATABASE_PASSWORD="your-secret"&lt;/p&gt;

&lt;p&gt;Environment variables are useful, but they aren't automatically a complete secrets-management solution.&lt;/p&gt;

&lt;p&gt;For sensitive production credentials, consider using a dedicated secrets manager, restricting access, and rotating credentials regularly.&lt;/p&gt;

&lt;p&gt;Lesson: Configuration should change between environments without requiring code changes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Successful Execution Doesn't Mean Correct Results&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is one of the most dangerous problems in data-processing applications.&lt;/p&gt;

&lt;p&gt;Imagine a web scraper that collects product information.&lt;/p&gt;

&lt;p&gt;The script runs successfully.&lt;/p&gt;

&lt;p&gt;No exceptions occur.&lt;/p&gt;

&lt;p&gt;The output file is created.&lt;/p&gt;

&lt;p&gt;Everything looks fine.&lt;/p&gt;

&lt;p&gt;Except the file contains only 8 products instead of the expected 200.&lt;/p&gt;

&lt;p&gt;Technically, the program completed.&lt;/p&gt;

&lt;p&gt;But from a business perspective, it failed.&lt;/p&gt;

&lt;p&gt;This is especially common in web scraping, ETL pipelines, API integrations, and scheduled data-processing jobs.&lt;/p&gt;

&lt;p&gt;Validate the output&lt;/p&gt;

&lt;p&gt;def validate_products(products):&lt;br&gt;
    if not isinstance(products, list):&lt;br&gt;
        raise TypeError(&lt;br&gt;
            "Expected a list of products"&lt;br&gt;
        )&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if not products:
    raise ValueError(
        "No products were collected"
    )

for index, product in enumerate(products):
    if not isinstance(product, dict):
        raise TypeError(
            f"Invalid product at index {index}"
        )

    required_fields = {
        "id",
        "name",
        "price"
    }

    missing = required_fields - product.keys()

    if missing:
        raise ValueError(
            f"Product {index} is missing: {missing}"
        )

return True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This example checks the basic structure of the collected data.&lt;/p&gt;

&lt;p&gt;In a real application, you might also validate:&lt;/p&gt;

&lt;p&gt;Field types and allowed values.&lt;/p&gt;

&lt;p&gt;Duplicate records.&lt;/p&gt;

&lt;p&gt;Unexpectedly low record counts.&lt;/p&gt;

&lt;p&gt;Missing identifiers.&lt;/p&gt;

&lt;p&gt;Data freshness.&lt;/p&gt;

&lt;p&gt;Sudden changes in price distributions.&lt;/p&gt;

&lt;p&gt;A nonempty list is not proof that a scraping job succeeded.&lt;/p&gt;

&lt;p&gt;And a zero exit code is not proof that the business requirements were satisfied.&lt;/p&gt;

&lt;p&gt;Lesson: Validate outcomes, not just execution.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your Script Needs a Recovery Strategy&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's imagine a Python application processing 10,000 products.&lt;/p&gt;

&lt;p&gt;After processing 8,500 products, the server restarts.&lt;/p&gt;

&lt;p&gt;What happens next?&lt;/p&gt;

&lt;p&gt;If the application starts from the beginning, it might repeat thousands of operations.&lt;/p&gt;

&lt;p&gt;If it doesn't save progress, some data could be lost.&lt;/p&gt;

&lt;p&gt;This is where a simple script starts becoming a reliable system.&lt;/p&gt;

&lt;p&gt;Introduce checkpoints&lt;/p&gt;

&lt;p&gt;A basic approach is to track completed product identifiers.&lt;/p&gt;

&lt;p&gt;import json&lt;br&gt;
from pathlib import Path&lt;/p&gt;

&lt;p&gt;CHECKPOINT_FILE = Path(&lt;br&gt;
    "completed_products.json"&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;def load_completed():&lt;br&gt;
    if not CHECKPOINT_FILE.exists():&lt;br&gt;
        return set()&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;with CHECKPOINT_FILE.open(
    "r",
    encoding="utf-8"
) as file:
    return set(json.load(file))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;def save_completed(completed):&lt;br&gt;
    temporary = CHECKPOINT_FILE.with_suffix(&lt;br&gt;
        ".tmp"&lt;br&gt;
    )&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;with temporary.open(
    "w",
    encoding="utf-8"
) as file:
    json.dump(
        sorted(completed),
        file
    )

temporary.replace(CHECKPOINT_FILE)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now a processing loop can skip previously completed records.&lt;/p&gt;

&lt;p&gt;completed = load_completed()&lt;/p&gt;

&lt;p&gt;for product_id in product_ids:&lt;br&gt;
    if product_id in completed:&lt;br&gt;
        continue&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;process_product(product_id)

completed.add(product_id)
save_completed(completed)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is a useful starting point for a single-process script.&lt;/p&gt;

&lt;p&gt;However, it has limitations.&lt;/p&gt;

&lt;p&gt;It assumes process_product() completes its work before the checkpoint is saved. If a crash occurs between an external side effect and the checkpoint update, that operation may be repeated.&lt;/p&gt;

&lt;p&gt;For this reason, production workflows should consider idempotency.&lt;/p&gt;

&lt;p&gt;An idempotent operation can be safely repeated without producing unwanted additional effects.&lt;/p&gt;

&lt;p&gt;For larger systems, database-backed job states, durable queues, and transactional processing are often more appropriate than local JSON files.&lt;/p&gt;

&lt;p&gt;Lesson: A reliable application should be designed for interruptions, not just successful runs.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A Practical Production Readiness Checklist&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before deploying a Python application, I find it useful to ask a few questions.&lt;/p&gt;

&lt;p&gt;Reliability&lt;/p&gt;

&lt;p&gt;Do external requests have appropriate timeouts?&lt;/p&gt;

&lt;p&gt;Are expected failures handled explicitly?&lt;/p&gt;

&lt;p&gt;Can interrupted operations be safely retried?&lt;/p&gt;

&lt;p&gt;Does the application validate its output?&lt;/p&gt;

&lt;p&gt;Configuration&lt;/p&gt;

&lt;p&gt;Are configuration values separated from source code?&lt;/p&gt;

&lt;p&gt;Are secrets excluded from the repository?&lt;/p&gt;

&lt;p&gt;Are dependency versions reproducible?&lt;/p&gt;

&lt;p&gt;Does the application work in the target environment?&lt;/p&gt;

&lt;p&gt;Observability&lt;/p&gt;

&lt;p&gt;Are important operations logged?&lt;/p&gt;

&lt;p&gt;Can failures be diagnosed from logs?&lt;/p&gt;

&lt;p&gt;Are sensitive values excluded from logs?&lt;/p&gt;

&lt;p&gt;Is there a way to detect repeated failures?&lt;/p&gt;

&lt;p&gt;Data integrity&lt;/p&gt;

&lt;p&gt;Can the application recover after a restart?&lt;/p&gt;

&lt;p&gt;Are duplicate operations handled safely?&lt;/p&gt;

&lt;p&gt;Is persistent data stored appropriately?&lt;/p&gt;

&lt;p&gt;Can incomplete results be detected?&lt;/p&gt;

&lt;p&gt;You don't need a complicated microservices architecture to address these problems.&lt;/p&gt;

&lt;p&gt;Even a small Python application benefits from these principles.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Difference Between a Script and a System&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One of the most valuable lessons in software development is understanding that a working implementation is not necessarily a reliable implementation.&lt;/p&gt;

&lt;p&gt;A script answers the question:&lt;/p&gt;

&lt;p&gt;"Can this task be automated?"&lt;/p&gt;

&lt;p&gt;A production-ready system must answer additional questions:&lt;/p&gt;

&lt;p&gt;"What happens when something goes wrong?"&lt;/p&gt;

&lt;p&gt;"Can we detect the problem?"&lt;/p&gt;

&lt;p&gt;"Can we recover without losing data?"&lt;/p&gt;

&lt;p&gt;"Can another developer maintain this six months later?"&lt;/p&gt;

&lt;p&gt;These questions fundamentally change how we design software.&lt;/p&gt;

&lt;p&gt;The code itself might represent only a small portion of the engineering effort.&lt;/p&gt;

&lt;p&gt;The rest involves understanding failure scenarios, infrastructure, dependencies, observability, and operational requirements.&lt;/p&gt;

&lt;p&gt;And that's why moving from a local Python script to a production environment is such an important learning experience.&lt;/p&gt;

&lt;p&gt;Final Thoughts&lt;/p&gt;

&lt;p&gt;Python makes it incredibly easy to build useful applications quickly.&lt;/p&gt;

&lt;p&gt;That's one of its greatest strengths.&lt;/p&gt;

&lt;p&gt;But the simplicity of writing Python code can sometimes hide the complexity of operating software in real environments.&lt;/p&gt;

&lt;p&gt;A script that works on your laptop is a good beginning.&lt;/p&gt;

&lt;p&gt;A system that continues working when networks fail, servers restart, dependencies change, and unexpected data appears is a much more significant achievement.&lt;/p&gt;

&lt;p&gt;The difference between a working script and reliable software isn't the number of lines of code. It's how well you've prepared for everything that can go wrong.&lt;/p&gt;

&lt;p&gt;And that mindset is what turns programming into software engineering.&lt;/p&gt;

&lt;p&gt;What's the most unexpected problem you've encountered when deploying a Python application?&lt;/p&gt;

&lt;p&gt;Was it an environment issue, an API failure, a dependency conflict, or something completely different?&lt;/p&gt;

&lt;p&gt;Share your experience in the comments. I'd love to hear about it.&lt;/p&gt;

</description>
      <category>python</category>
      <category>backend</category>
      <category>programming</category>
      <category>devops</category>
    </item>
    <item>
      <title>AI Makes Coding Faster. It Doesn't Necessarily Make Software Better.</title>
      <dc:creator>Maksim Pugin</dc:creator>
      <pubDate>Tue, 20 Oct 2026 08:45:13 +0000</pubDate>
      <link>https://dev.to/maksimpugin/ai-makes-coding-faster-it-doesnt-necessarily-make-software-better-2d67</link>
      <guid>https://dev.to/maksimpugin/ai-makes-coding-faster-it-doesnt-necessarily-make-software-better-2d67</guid>
      <description>&lt;p&gt;&lt;em&gt;We're getting better at generating code. But are we getting better at building software?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A few years ago, writing software meant spending a significant amount of time searching documentation, debugging syntax errors, and implementing repetitive logic.&lt;/p&gt;

&lt;p&gt;Today, AI can generate an API endpoint, write a database query, explain an unfamiliar codebase, or produce a working prototype in seconds.&lt;/p&gt;

&lt;p&gt;That's impressive.&lt;/p&gt;

&lt;p&gt;But there's a question I think deserves more attention:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If writing code is becoming easier, why is building reliable software still so difficult?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer is that generating code and engineering software are two different problems.&lt;/p&gt;

&lt;p&gt;And AI is changing one much faster than the other.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Illusion of Productivity
&lt;/h2&gt;

&lt;p&gt;Imagine two developers working on the same application.&lt;/p&gt;

&lt;p&gt;Developer A writes most of the code manually. They spend time planning the architecture, defining interfaces, and thinking through potential failures.&lt;/p&gt;

&lt;p&gt;Developer B uses AI to generate the majority of the implementation.&lt;/p&gt;

&lt;p&gt;After two hours, Developer B has significantly more code.&lt;/p&gt;

&lt;p&gt;But does that mean the project is further along?&lt;/p&gt;

&lt;p&gt;Not necessarily.&lt;/p&gt;

&lt;p&gt;Lines of code are not a meaningful measure of software quality.&lt;/p&gt;

&lt;p&gt;What matters is whether the application is correct, maintainable, secure, and capable of handling real-world conditions.&lt;/p&gt;

&lt;p&gt;AI can dramatically reduce implementation time while leaving many engineering decisions unresolved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The dangerous assumption is that faster implementation automatically means faster delivery.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sometimes it does.&lt;/p&gt;

&lt;p&gt;Sometimes it simply moves the complexity into debugging, integration, and maintenance.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. A Working Prototype Is Not a Reliable System
&lt;/h2&gt;

&lt;p&gt;Consider a simple Python web scraper.&lt;/p&gt;

&lt;p&gt;You ask an AI assistant:&lt;/p&gt;

&lt;p&gt;"Create a Python script that extracts product names and prices from an e-commerce website."&lt;/p&gt;

&lt;p&gt;Within seconds, you receive something that looks reasonable.&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bs4&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://example.com/products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;soup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BeautifulSoup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;html.parser&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.product&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a simple static page with matching selectors, this might work.&lt;/p&gt;

&lt;p&gt;But now consider a real marketplace.&lt;/p&gt;

&lt;p&gt;What happens when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Products are loaded dynamically?&lt;/li&gt;
&lt;li&gt;Prices change based on customer location?&lt;/li&gt;
&lt;li&gt;Some products have multiple price types?&lt;/li&gt;
&lt;li&gt;A request times out halfway through execution?&lt;/li&gt;
&lt;li&gt;The HTML structure changes?&lt;/li&gt;
&lt;li&gt;The same product appears multiple times?&lt;/li&gt;
&lt;li&gt;The scraper returns incomplete data without raising an exception?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The initial script doesn't address these problems.&lt;/p&gt;

&lt;p&gt;And that is not necessarily a failure of AI.&lt;/p&gt;

&lt;p&gt;It's a failure to define the actual engineering requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI can generate an implementation of the problem you describe. It cannot guarantee that you've described the right problem.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Architecture Is Where the Real Work Begins
&lt;/h2&gt;

&lt;p&gt;While developing a Python application for collecting marketplace data, I encountered a challenge that goes beyond HTML parsing.&lt;/p&gt;

&lt;p&gt;The application wasn't just a scraper.&lt;/p&gt;

&lt;p&gt;It involved several connected components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browser automation with Selenium.&lt;/li&gt;
&lt;li&gt;HTML parsing with BeautifulSoup.&lt;/li&gt;
&lt;li&gt;Data validation and processing.&lt;/li&gt;
&lt;li&gt;CSV and JSON exports.&lt;/li&gt;
&lt;li&gt;User authentication and MySQL storage.&lt;/li&gt;
&lt;li&gt;A desktop interface built with Tkinter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each component was manageable individually.&lt;/p&gt;

&lt;p&gt;The difficulty came from making them work together reliably.&lt;/p&gt;

&lt;p&gt;For example, browser automation can take time. If it runs directly in the graphical interface's main thread, the application may become unresponsive.&lt;/p&gt;

&lt;p&gt;Moving that operation into a background thread improves responsiveness, but introduces additional questions.&lt;/p&gt;

&lt;p&gt;How should results be communicated to the interface?&lt;/p&gt;

&lt;p&gt;What happens when a user cancels an operation?&lt;/p&gt;

&lt;p&gt;How should partial results be handled?&lt;/p&gt;

&lt;p&gt;How do we prevent concurrent tasks from corrupting shared state?&lt;/p&gt;

&lt;p&gt;These are architectural decisions.&lt;/p&gt;

&lt;p&gt;AI can suggest solutions, but the developer still needs to evaluate the trade-offs.&lt;/p&gt;

&lt;p&gt;A useful design principle is to separate responsibilities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Interface
      |
      v
Application Controller
      |
      v
Scraping Service
      |
      v
Data Validation
      |
      v
Storage / Export
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure isn't automatically the best architecture for every project.&lt;/p&gt;

&lt;p&gt;But it creates boundaries that make the application easier to reason about.&lt;/p&gt;

&lt;p&gt;And reasoning about a system is something developers cannot afford to outsource completely.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The Problem with Code You Don't Understand
&lt;/h2&gt;

&lt;p&gt;One of the most interesting effects of AI-assisted development is how easy it becomes to accept code that looks correct.&lt;/p&gt;

&lt;p&gt;A generated function may have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean formatting.&lt;/li&gt;
&lt;li&gt;Descriptive variable names.&lt;/li&gt;
&lt;li&gt;Type annotations.&lt;/li&gt;
&lt;li&gt;Helpful comments.&lt;/li&gt;
&lt;li&gt;A convincing explanation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these guarantee correctness.&lt;/p&gt;

&lt;p&gt;Consider a function that calculates a product discount:&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;def&lt;/span&gt; &lt;span class="nf"&gt;discount_percent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;original&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;original&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;original&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks fine, right?&lt;/p&gt;

&lt;p&gt;But what happens if &lt;code&gt;original&lt;/code&gt; is zero?&lt;/p&gt;

&lt;p&gt;What if one of the prices is missing?&lt;/p&gt;

&lt;p&gt;What if the price is represented as a localized string?&lt;/p&gt;

&lt;p&gt;What if the current price is higher than the original price?&lt;/p&gt;

&lt;p&gt;The function solves the simplest version of the problem.&lt;/p&gt;

&lt;p&gt;A reliable implementation requires explicit decisions about invalid input, numerical precision, and business rules.&lt;/p&gt;

&lt;p&gt;The same principle applies to AI-generated database queries, API integrations, authentication logic, and infrastructure configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code that looks professional is not necessarily production-ready code.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. AI Changes the Developer's Responsibility
&lt;/h2&gt;

&lt;p&gt;I don't think the solution is to avoid AI.&lt;/p&gt;

&lt;p&gt;Quite the opposite.&lt;/p&gt;

&lt;p&gt;AI is useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exploring unfamiliar libraries.&lt;/li&gt;
&lt;li&gt;Generating boilerplate.&lt;/li&gt;
&lt;li&gt;Suggesting refactoring approaches.&lt;/li&gt;
&lt;li&gt;Creating initial test cases.&lt;/li&gt;
&lt;li&gt;Explaining errors.&lt;/li&gt;
&lt;li&gt;Reviewing alternative implementations.&lt;/li&gt;
&lt;li&gt;Accelerating documentation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But I believe its greatest value appears when developers treat it as an engineering assistant rather than an unquestionable authority.&lt;/p&gt;

&lt;p&gt;A more reliable workflow looks like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Define the problem.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Clarify inputs, outputs, constraints, and expected behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Design the architecture.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Decide which components are needed and how they interact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Use AI to accelerate implementation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Generate focused components rather than blindly producing an entire application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Review the result.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Understand the generated code and challenge its assumptions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Test real failure scenarios.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Validate edge cases, integration behavior, and error handling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6: Measure the outcome.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Evaluate correctness, performance, maintainability, and actual time saved.&lt;/p&gt;

&lt;p&gt;This workflow may feel slower than generating an entire application from one prompt.&lt;/p&gt;

&lt;p&gt;But it can prevent expensive problems later.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. The New Engineering Bottleneck
&lt;/h2&gt;

&lt;p&gt;Here's the interesting part.&lt;/p&gt;

&lt;p&gt;As code generation becomes cheaper, the relative importance of other engineering activities increases.&lt;/p&gt;

&lt;p&gt;Understanding requirements.&lt;/p&gt;

&lt;p&gt;Designing system boundaries.&lt;/p&gt;

&lt;p&gt;Validating behavior.&lt;/p&gt;

&lt;p&gt;Managing complexity.&lt;/p&gt;

&lt;p&gt;Making trade-offs.&lt;/p&gt;

&lt;p&gt;Debugging unexpected interactions.&lt;/p&gt;

&lt;p&gt;These activities were always important.&lt;/p&gt;

&lt;p&gt;AI simply makes the difference between writing code and engineering software more visible.&lt;/p&gt;

&lt;p&gt;The bottleneck is gradually shifting from implementation toward judgment and verification.&lt;/p&gt;

&lt;p&gt;This also changes what developers need to learn.&lt;/p&gt;

&lt;p&gt;Knowing syntax remains useful.&lt;/p&gt;

&lt;p&gt;But understanding how systems behave becomes even more valuable.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. My Prediction: Software Engineering Will Become More About Decisions
&lt;/h2&gt;

&lt;p&gt;I don't believe AI will make software engineering irrelevant.&lt;/p&gt;

&lt;p&gt;I believe it will change which engineering skills matter most.&lt;/p&gt;

&lt;p&gt;Developers who only translate detailed instructions into code may face increasing automation.&lt;/p&gt;

&lt;p&gt;Developers who can understand complex problems, design reliable systems, evaluate generated solutions, and take responsibility for outcomes will remain valuable.&lt;/p&gt;

&lt;p&gt;The future of software development isn't necessarily about writing every line manually.&lt;/p&gt;

&lt;p&gt;It's about knowing which lines should exist, why they exist, and whether they actually solve the problem.&lt;/p&gt;

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

&lt;p&gt;AI has made it easier than ever to create something that works.&lt;/p&gt;

&lt;p&gt;But creating something that works once is not the same as creating something that works reliably.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The real measure of engineering productivity isn't how quickly we generate code. It's how effectively we turn requirements into dependable software.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm interested in how other developers are experiencing this shift.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Has AI actually reduced the total time you spend delivering software, or has it moved more of your time into reviewing and debugging generated code?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'd love to hear your experience.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>softwareengineering</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Building a Web Scraper with Python: Lessons from a Real Project</title>
      <dc:creator>Maksim Pugin</dc:creator>
      <pubDate>Tue, 20 Oct 2026 08:23:09 +0000</pubDate>
      <link>https://dev.to/maksimpugin/building-a-web-scraper-with-python-lessons-from-a-real-project-kd3</link>
      <guid>https://dev.to/maksimpugin/building-a-web-scraper-with-python-lessons-from-a-real-project-kd3</guid>
      <description>&lt;h1&gt;
  
  
  Building a Web Scraper with Python: Lessons from a Real Project
&lt;/h1&gt;

&lt;p&gt;When I started developing my own web scraping application, I thought the hardest part would be extracting data from websites.&lt;/p&gt;

&lt;p&gt;I was wrong.&lt;/p&gt;

&lt;p&gt;Extracting information is relatively straightforward. Building a system that can consistently collect, process, store, and present that information is a completely different challenge.&lt;/p&gt;

&lt;p&gt;In this article, I'd like to share some lessons from developing a Python-based desktop application for collecting and analyzing marketplace product data.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Idea Behind the Project
&lt;/h2&gt;

&lt;p&gt;The initial idea was simple: create an application that could automatically collect product information from online marketplaces and present it in a structured format.&lt;/p&gt;

&lt;p&gt;The application needed to handle several tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search for products based on user queries.&lt;/li&gt;
&lt;li&gt;Collect product names, prices, discounts, and ratings.&lt;/li&gt;
&lt;li&gt;Process and structure the extracted information.&lt;/li&gt;
&lt;li&gt;Export results to CSV and JSON.&lt;/li&gt;
&lt;li&gt;Provide a graphical interface for interacting with the collected data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, as development progressed, the project became much more interesting from an engineering perspective.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the Technology Stack
&lt;/h2&gt;

&lt;p&gt;I decided to build the application using Python and several libraries:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Python&lt;/strong&gt; — the main programming language.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Selenium&lt;/strong&gt; — browser automation and interaction with dynamically rendered pages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BeautifulSoup&lt;/strong&gt; — parsing HTML and extracting structured information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MySQL&lt;/strong&gt; — storing user accounts and profile information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tkinter and ttkbootstrap&lt;/strong&gt; — creating the desktop interface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pandas and CSV/JSON tools&lt;/strong&gt; — potential options for further data processing, with CSV and JSON already used for exporting results.&lt;/p&gt;

&lt;p&gt;Each technology solves a specific problem, but the real challenge is making all these components work together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge #1: Modern Websites Are Dynamic
&lt;/h2&gt;

&lt;p&gt;One of the first problems I encountered was that many marketplace pages don't expose all their product information in the initial HTML response.&lt;/p&gt;

&lt;p&gt;Content may be loaded dynamically as users scroll or interact with the page.&lt;/p&gt;

&lt;p&gt;This means that simply downloading the HTML using an HTTP request is not always enough.&lt;/p&gt;

&lt;p&gt;My approach involved using Selenium to interact with the browser, scrolling through product listings, collecting product URLs, and then extracting information from individual product pages.&lt;/p&gt;

&lt;p&gt;The key lesson?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Web scraping is often about understanding how a website behaves, not just how its HTML is structured.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge #2: Data Is Rarely Consistent
&lt;/h2&gt;

&lt;p&gt;Another interesting challenge was processing product information.&lt;/p&gt;

&lt;p&gt;For example, a product might have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A regular price.&lt;/li&gt;
&lt;li&gt;A discounted price.&lt;/li&gt;
&lt;li&gt;A special price available through a marketplace payment method.&lt;/li&gt;
&lt;li&gt;A discount percentage.&lt;/li&gt;
&lt;li&gt;A rating and review count.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not every product contains every field, and the HTML structure can vary.&lt;/p&gt;

&lt;p&gt;To handle this, I had to implement additional validation and data-cleaning logic.&lt;/p&gt;

&lt;p&gt;A scraper that extracts information is useful.&lt;/p&gt;

&lt;p&gt;A scraper that extracts &lt;em&gt;reliable and consistently structured information&lt;/em&gt; is significantly more valuable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge #3: Architecture Matters
&lt;/h2&gt;

&lt;p&gt;At first, it may seem reasonable to put the entire scraping process into a single script.&lt;/p&gt;

&lt;p&gt;But once the application includes authentication, database operations, browser automation, data processing, and a graphical interface, this approach becomes difficult to maintain.&lt;/p&gt;

&lt;p&gt;I started separating responsibilities into logical components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User authentication and profile management.&lt;/li&gt;
&lt;li&gt;Browser automation and URL collection.&lt;/li&gt;
&lt;li&gt;Product information extraction.&lt;/li&gt;
&lt;li&gt;Data processing and export.&lt;/li&gt;
&lt;li&gt;Graphical user interface.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This made the project easier to understand, debug, and extend.&lt;/p&gt;

&lt;p&gt;I also used background threads to prevent long-running scraping operations from freezing the interface.&lt;/p&gt;

&lt;p&gt;One important consideration is keeping GUI updates on the main Tkinter thread, while passing results from background operations safely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge #4: Reliability Is More Important Than Speed
&lt;/h2&gt;

&lt;p&gt;One of the most important things I learned is that collecting data quickly is not necessarily the same as collecting it successfully.&lt;/p&gt;

&lt;p&gt;A scraping application needs to account for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network failures.&lt;/li&gt;
&lt;li&gt;Unexpected HTML changes.&lt;/li&gt;
&lt;li&gt;Missing product information.&lt;/li&gt;
&lt;li&gt;Browser timeouts.&lt;/li&gt;
&lt;li&gt;Duplicate records.&lt;/li&gt;
&lt;li&gt;Partial execution failures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A production-oriented approach should also respect website policies, reasonable request rates, and access restrictions.&lt;/p&gt;

&lt;p&gt;These considerations are essential if the application is expected to operate reliably over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;I'm interested in taking this project further.&lt;/p&gt;

&lt;p&gt;My long-term goal is to move beyond simple data collection and develop a system capable of analyzing marketplace information and identifying useful patterns.&lt;/p&gt;

&lt;p&gt;Potential development directions include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Historical price tracking.&lt;/li&gt;
&lt;li&gt;Product comparison across marketplaces.&lt;/li&gt;
&lt;li&gt;Automated detection of price changes.&lt;/li&gt;
&lt;li&gt;AI-assisted product classification.&lt;/li&gt;
&lt;li&gt;Recommendation algorithms.&lt;/li&gt;
&lt;li&gt;A more scalable data-processing architecture.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most interesting part is the transition from a basic scraping tool to a complete data-driven product.&lt;/p&gt;

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

&lt;p&gt;This project reminded me of something important:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Writing code that works is only the first step. Designing software that remains understandable, maintainable, and useful is the real engineering challenge.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm continuing to explore Python, automation, AI, and enterprise software development, and I plan to share more practical engineering experiences here.&lt;/p&gt;

&lt;p&gt;If you've worked on web scraping or data collection systems, I'd be interested to hear:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What was the biggest technical challenge you encountered when building a scraper?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>python</category>
      <category>webdev</category>
      <category>automation</category>
      <category>webscraping</category>
    </item>
  </channel>
</rss>
