<?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: Negitama</title>
    <description>The latest articles on DEV Community by Negitama (@negitamaai).</description>
    <link>https://dev.to/negitamaai</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%2F3258232%2Fa5f8d307-4834-4aeb-90a7-5a3c624409f6.png</url>
      <title>DEV Community: Negitama</title>
      <link>https://dev.to/negitamaai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/negitamaai"/>
    <language>en</language>
    <item>
      <title>I wish I knew this before: Python's ORM vs Raw SQL in SQLAlchemy Explained!</title>
      <dc:creator>Negitama</dc:creator>
      <pubDate>Sat, 14 Jun 2025 14:38:00 +0000</pubDate>
      <link>https://dev.to/negitamaai/i-wish-i-knew-this-before-pythons-orm-vs-raw-sql-in-sqlalchemy-explained-4n0j</link>
      <guid>https://dev.to/negitamaai/i-wish-i-knew-this-before-pythons-orm-vs-raw-sql-in-sqlalchemy-explained-4n0j</guid>
      <description>&lt;h1&gt;
  
  
  I wish I knew this before: Python's ORM vs Raw SQL in SQLAlchemy Explained!\n\n## Introduction\n\nIf you’ve ever built a Python backend, you’ve likely run into the choice between using an ORM (Object Relational Mapper) or writing raw SQL queries when working with a database. Let’s dive into a hands-on example using SQLAlchemy and unravel the strengths and weaknesses of each approach—so you can pick what’s best for your next project!\n\n---\n\n## 1. Setup: Define Models with SQLAlchemy ORM\n\nFirst, let’s set up our data model using SQLAlchemy’s declarative base.\n\n
&lt;/h1&gt;

&lt;p&gt;&lt;br&gt;
&lt;code&gt;python\nfrom sqlalchemy import Column, Integer, String, Text, create_engine\nfrom sqlalchemy.ext.declarative import declarative_base\nfrom sqlalchemy.orm import sessionmaker\n\nBase = declarative_base()\n\nclass JobDescription(Base):\n    __tablename__ = 'job_description'\n    id = Column(Integer, primary_key=True)\n    company_id = Column(String, nullable=False)\n    company_name = Column(Text, nullable=False)\n    job_description = Column(Text, nullable=False)\n\nclass CandidateResume(Base):\n    __tablename__ = 'candidate_resume'\n    id = Column(Integer, primary_key=True)\n    candidate_id = Column(String, nullable=False)\n    candidate_name = Column(Text, nullable=False)\n    resume = Column(Text, nullable=False)\n\nengine = create_engine('postgresql://user:password@localhost/dbname')\nSession = sessionmaker(bind=engine)\n\nif __name__ == '__main__':\n    Base.metadata.create_all(engine)\n&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
\n\n---\n\n## 2. Bit-ORM Layer: Repository Using the ORM\n\nUsing the ORM, you’ll interact with Python objects instead of raw SQL.\n\n&lt;br&gt;
&lt;br&gt;
&lt;code&gt;python\nfrom orm_models import JobDescription, Session\n\ndef get_job_descriptions_by_company_orm(company_id: str):\n    """\n    Retrieve JobDescriptions for a given company using ORM.\n    Returns a list of JobDescription objects.\n    """\n    session = Session()\n    try:\n        results = session.query(JobDescription)\n                         .filter(JobDescription.company_id == company_id)\n                         .all()\n        return results\n    finally:\n        session.close()\n\nif __name__ == '__main__':\n    jobs = get_job_descriptions_by_company_orm('COMPANY_123')\n    for job in jobs:\n        print(f"{job.company_name}: {job.job_description}")\n&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
\n\n### Advantages of the ORM Approach\n- &lt;strong&gt;Abstraction &amp;amp; Simplicity:&lt;/strong&gt; Work directly with Python objects; SQL generation is handled for you.\n- &lt;strong&gt;Maintainability:&lt;/strong&gt; Updates in models propagate everywhere queries are used.\n- &lt;strong&gt;Safety &amp;amp; Consistency:&lt;/strong&gt; Automatic parameter binding reduces SQL injection risks.\n- &lt;strong&gt;Relationship Handling:&lt;/strong&gt; Easier navigation between related models.\n\n### Disadvantages\n- &lt;strong&gt;Abstraction Overhead:&lt;/strong&gt; Less efficient for highly-optimized or complex queries.\n- &lt;strong&gt;Difficult Advanced Queries:&lt;/strong&gt; May require raw SQL for complex performance customizations.\n\n---\n\n## 3. Bit-Out-ORM Layer: Repository Using Raw SQL\n\nFor cases where fine-grained control is needed, writing raw SQL lets you directly manipulate queries.\n\n&lt;br&gt;
&lt;br&gt;
&lt;code&gt;python\nfrom orm_models import engine\n\ndef get_job_descriptions_by_company_raw(company_id: str):\n    """\n    Retrieve job descriptions for a given company using raw SQL.\n    Returns a list of RowProxy objects.\n    """\n    with engine.connect() as connection:\n        result = connection.execute(\n            "SELECT id, company_id, company_name, job_description FROM job_description WHERE company_id = :company_id",\n            {"company_id": company_id}\n        )\n        return result.fetchall()\n\nif __name__ == '__main__':\n    rows = get_job_descriptions_by_company_raw('COMPANY_123')\n    for row in rows:\n        print(f"{row['company_name']}: {row['job_description']}")\n&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
\n\n### Advantages of the Raw SQL Approach\n- &lt;strong&gt;Fine-Grained Control:&lt;/strong&gt; Ideal for advanced optimizations and database-specific features.\n- &lt;strong&gt;Transparency:&lt;/strong&gt; Directly see and understand the executed SQL.\n- &lt;strong&gt;Flexibility:&lt;/strong&gt; Access features not surfaced by the ORM.\n\n### Disadvantages\n- &lt;strong&gt;Manual Mapping:&lt;/strong&gt; Responsible for converting rows into Python objects.\n- &lt;strong&gt;Error-Prone:&lt;/strong&gt; Handwriting SQL increases the chance of errors and maintenance headaches.\n- &lt;strong&gt;Repetition:&lt;/strong&gt; CRUD operations may become verbose and repetitive.\n\n---\n\n## 4. Summary &amp;amp; Takeaways\n\n- Use &lt;strong&gt;ORM&lt;/strong&gt; for productivity, maintainability, and safety in most cases.\n- Use &lt;strong&gt;Raw SQL&lt;/strong&gt; for advanced queries or performance-critical areas.\n- Combining both approaches in a hybrid system gives the best of both worlds!\n\n*&lt;em&gt;Did you find this breakdown useful? Drop your favorite approach in the comments below!&lt;/em&gt;*\n\nHappy coding! 🚀&lt;/p&gt;

</description>
      <category>python</category>
      <category>database</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I wish I knew this before mixing SQLAlchemy ORM with raw SQL: Python Database Access, Two Ways</title>
      <dc:creator>Negitama</dc:creator>
      <pubDate>Sat, 14 Jun 2025 14:31:30 +0000</pubDate>
      <link>https://dev.to/negitamaai/i-wish-i-knew-this-before-mixing-sqlalchemy-orm-with-raw-sql-python-database-access-two-ways-37d7</link>
      <guid>https://dev.to/negitamaai/i-wish-i-knew-this-before-mixing-sqlalchemy-orm-with-raw-sql-python-database-access-two-ways-37d7</guid>
      <description>&lt;h1&gt;
  
  
  I wish I knew this before mixing SQLAlchemy ORM with raw SQL: Python Database Access, Two Ways\n\nWorking with databases in Python? You’ve probably used SQLAlchemy, but did you know there are two powerful approaches: the ORM layer and the raw SQL layer? Here’s a deep dive into both, including code, pros &amp;amp; cons, and when to use each.\n\n## 1. Setup: Define Models with SQLAlchemy ORM\nFirst, we create our ORM models. This file (e.g., &lt;code&gt;orm_models.py&lt;/code&gt;) defines the schema using SQLAlchemy’s declarative base.\n\n
&lt;/h1&gt;

&lt;p&gt;&lt;br&gt;
&lt;code&gt;python\nfrom sqlalchemy import Column, Integer, String, Text, create_engine\nfrom sqlalchemy.ext.declarative import declarative_base\nfrom sqlalchemy.orm import sessionmaker\n\nBase = declarative_base()\n\nclass JobDescription(Base):\n    __tablename__ = 'job_description'\n    id = Column(Integer, primary_key=True)\n    company_id = Column(String, nullable=False)\n    company_name = Column(Text, nullable=False)\n    job_description = Column(Text, nullable=False)\n\nclass CandidateResume(Base):\n    __tablename__ = 'candidate_resume'\n    id = Column(Integer, primary_key=True)\n    candidate_id = Column(String, nullable=False)\n    candidate_name = Column(Text, nullable=False)\n    resume = Column(Text, nullable=False)\n\nengine = create_engine('postgresql://user:password@localhost/dbname')\nSession = sessionmaker(bind=engine)\n\nif __name__ == '__main__':\n    Base.metadata.create_all(engine)\n&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
\n\n## 2. Bit-ORM Layer: Repository Using the ORM\nUse Python objects instead of SQL!\n\n&lt;br&gt;
&lt;br&gt;
&lt;code&gt;python\nfrom orm_models import JobDescription, Session\n\ndef get_job_descriptions_by_company_orm(company_id: str):\n    session = Session()\n    try:\n        results = session.query(JobDescription)\n                         .filter(JobDescription.company_id == company_id)\n                         .all()\n        return results\n    finally:\n        session.close()\n\nif __name__ == '__main__':\n    jobs = get_job_descriptions_by_company_orm('COMPANY_123')\n    for job in jobs:\n        print(f"{job.company_name}: {job.job_description}")\n&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
\n\n*&lt;em&gt;Advantages:&lt;/em&gt;&lt;em&gt;\n- Abstraction &amp;amp; Simplicity: Deal with Python objects, not SQL.\n- Maintainability: Model changes update all queries.\n- Safety: Built-in SQL injection protection.\n- Relationship Handling: Easier joins and associations.\n\n&lt;/em&gt;&lt;em&gt;Disadvantages:&lt;/em&gt;&lt;em&gt;\n- Overhead: Less efficient for fine-tuned queries.\n- Complexity: Advanced queries can get tricky.\n\n## 3. Bit-Out-ORM Layer: Repository Using Raw SQL\nControl and performance!\n\n&lt;br&gt;
&lt;br&gt;
&lt;code&gt;python\nfrom orm_models import engine\n\ndef get_job_descriptions_by_company_raw(company_id: str):\n    with engine.connect() as connection:\n        result = connection.execute(\n            "SELECT id, company_id, company_name, job_description FROM job_description WHERE company_id = :company_id",\n            {"company_id": company_id}\n        )\n        return result.fetchall()\n\nif __name__ == '__main__':\n    rows = get_job_descriptions_by_company_raw('COMPANY_123')\n    for row in rows:\n        print(f"{row['company_name']}: {row['job_description']}")\n&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
\n\n&lt;/em&gt;&lt;em&gt;Advantages:&lt;/em&gt;&lt;em&gt;\n- Fine-Grained Control: Optimize complex queries.\n- Transparency: See the real SQL sent.\n- Flexibility: Use database-specific features.\n\n&lt;/em&gt;&lt;em&gt;Disadvantages:&lt;/em&gt;&lt;em&gt;\n- Manual Mapping: More code, more risk.\n- Error-Prone: Easy to make mistakes.\n- Repetition: CRUD operations are verbose.\n\n## 4. Summary\nFor most use-cases, use the ORM. For performance-heavy or database-specific tasks, write raw SQL. Combine both for the best of both worlds!\n\n&lt;/em&gt;&lt;em&gt;Which approach do you prefer? Let me know in the comments!&lt;/em&gt;*&lt;/p&gt;

</description>
      <category>python</category>
      <category>database</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I wish I knew this before approaching Database Access in Python with SQLAlchemy</title>
      <dc:creator>Negitama</dc:creator>
      <pubDate>Sat, 14 Jun 2025 14:29:46 +0000</pubDate>
      <link>https://dev.to/negitamaai/i-wish-i-knew-this-before-approaching-database-access-in-python-with-sqlalchemy-1enk</link>
      <guid>https://dev.to/negitamaai/i-wish-i-knew-this-before-approaching-database-access-in-python-with-sqlalchemy-1enk</guid>
      <description>&lt;h1&gt;
  
  
  I wish I knew this before approaching Database Access in Python with SQLAlchemy\n\nBelow is an in-depth example that shows two approaches in Python using SQLAlchemy: one that leverages the ORM ("bit-ORM layer") and one that uses raw SQL queries ("bit-out-ORM layer"). In the example, we define a simple model for job descriptions and then provide two repository functions that return job descriptions for a given company. At the end, I discuss the advantages and disadvantages of each approach.\n\n## 1. Setup: Define Models with SQLAlchemy ORM\n\nFirst, we create our ORM models. This file (e.g., orm_models.py) defines the schema in code using SQLAlchemy’s declarative base.\n
&lt;/h1&gt;

&lt;p&gt;&lt;br&gt;
&lt;code&gt;python\nfrom sqlalchemy import Column, Integer, String, Text, create_engine\nfrom sqlalchemy.ext.declarative import declarative_base\nfrom sqlalchemy.orm import sessionmaker\n\n# Base class for our ORM models\nBase = declarative_base()\n\nclass JobDescription(Base):\n    __tablename__ = 'job_description'\n    id = Column(Integer, primary_key=True)\n    company_id = Column(String, nullable=False)\n    company_name = Column(Text, nullable=False)\n    job_description = Column(Text, nullable=False)\n\nclass CandidateResume(Base):\n    __tablename__ = 'candidate_resume'\n    id = Column(Integer, primary_key=True)\n    candidate_id = Column(String, nullable=False)\n    candidate_name = Column(Text, nullable=False)\n    resume = Column(Text, nullable=False)\n\n# Create the engine (adjust connection string as needed)\nengine = create_engine('postgresql://user:password@localhost/dbname')\n\n# Create a configured "Session" class\nSession = sessionmaker(bind=engine)\n\n# Optionally, create tables in the database (for development)\nif __name__ == '__main__':\n    Base.metadata.create_all(engine)\n&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
\n\n## 2. Bit-ORM Layer: Repository Using the ORM\n\nThis repository function uses the ORM—meaning we work with Python objects. The ORM layer automatically converts our model objects into the appropriate SQL queries. Save this in (e.g.) repository_orm.py.\n&lt;br&gt;
&lt;br&gt;
&lt;code&gt;python\nfrom orm_models import JobDescription, Session\n\ndef get_job_descriptions_by_company_orm(company_id: str):\n    """\n    Retrieve JobDescriptions for a given company using ORM.\n    Returns a list of JobDescription objects.\n    """\n    session = Session()\n    try:\n        results = session.query(JobDescription)\n                         .filter(JobDescription.company_id == company_id)\n                         .all()\n        return results\n    finally:\n        session.close()\n\n# Example usage:\nif __name__ == '__main__':\n    jobs = get_job_descriptions_by_company_orm('COMPANY_123')\n    for job in jobs:\n        print(f"{job.company_name}: {job.job_description}")\n&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
\n\n### Advantages of the ORM Approach\n\nAbstraction &amp;amp; Simplicity: You deal with Python objects; the ORM hides the SQL details.\nMaintainability: Changes to models update all the underlying queries.\nSafety &amp;amp; Consistency: Automatic parameter binding prevents SQL injection.\nRelationship Handling: ORMs make it easier to navigate relationships among models.\n\n### Disadvantages of the ORM Approach\n\nAbstraction Overhead: Sometimes it can be less efficient if you need very fine-tuned queries.\nComplexity for Advanced Queries: Very complex queries or performance optimizations may require custom SQL.\n\n## 3. Bit-Out-ORM Layer: Repository Using Raw SQL\n\nIn contrast, this repository function uses raw SQL queries via SQLAlchemy’s connection API. Save this as repository_raw.py.\n&lt;br&gt;
&lt;br&gt;
&lt;code&gt;python\nfrom orm_models import engine\n\ndef get_job_descriptions_by_company_raw(company_id: str):\n    """\n    Retrieve job descriptions for a given company using raw SQL.\n    Returns a list of RowProxy objects.\n    """\n    with engine.connect() as connection:\n        # Use parameterized queries to avoid SQL injection\n        result = connection.execute(\n            "SELECT id, company_id, company_name, job_description FROM job_description WHERE company_id = :company_id",\n            {"company_id": company_id}\n        )\n        return result.fetchall()\n\n# Example usage:\nif __name__ == '__main__':\n    rows = get_job_descriptions_by_company_raw('COMPANY_123')\n    for row in rows:\n        print(f"{row['company_name']}: {row['job_description']}")\n&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
\n\n### Advantages of the Raw SQL Approach\n\nFine-Grained Control: Directly write and optimize SQL queries for complex or performance-critical tasks.\nTransparency: You see exactly what SQL is sent to the database.\nFlexibility: Use database-specific features or functions not directly exposed by the ORM.\n\n### Disadvantages of the Raw SQL Approach\n\nManual Mapping: You must manually convert results to Python objects if needed.\nError-Prone: Writing raw SQL can lead to mistakes, and maintaining it over time may be more challenging.\nRepetition: Common CRUD operations might require writing repetitive SQL queries when an ORM could generate them automatically.\n\n## 4. Summary and Comparison\n\nIn a full-scale system, it’s common to use an ORM for most database operations while falling back on raw SQL for advanced use cases that require fine-tuning. This hybrid approach allows you to benefit from high productivity when possible while retaining control when needed.&lt;/p&gt;

</description>
      <category>python</category>
      <category>database</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Did you know the two powerful ways in Python to interact with databases?</title>
      <dc:creator>Negitama</dc:creator>
      <pubDate>Sat, 14 Jun 2025 14:24:47 +0000</pubDate>
      <link>https://dev.to/negitamaai/did-you-know-the-two-powerful-ways-in-python-to-interact-with-databases-57kl</link>
      <guid>https://dev.to/negitamaai/did-you-know-the-two-powerful-ways-in-python-to-interact-with-databases-57kl</guid>
      <description>&lt;p&gt;Did you know… In Python, there are two powerful ways to interact with databases using SQLAlchemy: the ORM layer and raw SQL queries. Here’s a deep dive into both approaches and when to use each.\n\n# Setup: Define Models with SQLAlchemy ORM\nFirst, we create our ORM models. This file (e.g., orm_models.py) defines the schema in code using SQLAlchemy’s declarative base.\n\n&lt;br&gt;
&lt;br&gt;
&lt;code&gt;python\nfrom sqlalchemy import Column, Integer, String, Text, create_engine\nfrom sqlalchemy.ext.declarative import declarative_base\nfrom sqlalchemy.orm import sessionmaker\n\n# Base class for our ORM models\nBase = declarative_base()\n\nclass JobDescription(Base):\n    __tablename__ = 'job_description'\n    id = Column(Integer, primary_key=True)\n    company_id = Column(String, nullable=False)\n    company_name = Column(Text, nullable=False)\n    job_description = Column(Text, nullable=False)\n\nclass CandidateResume(Base):\n    __tablename__ = 'candidate_resume'\n    id = Column(Integer, primary_key=True)\n    candidate_id = Column(String, nullable=False)\n    candidate_name = Column(Text, nullable=False)\n    resume = Column(Text, nullable=False)\n\n# Create the engine (adjust connection string as needed)\nengine = create_engine('postgresql://user:password@localhost/dbname')\n\n# Create a configured "Session" class\nSession = sessionmaker(bind=engine)\n\n# Optionally, create tables in the database (for development)\nif __name__ == '__main__':\n    Base.metadata.create_all(engine)\n&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
\n\n# Bit-ORM Layer: Repository Using the ORM\nThis repository function uses the ORM—meaning we work with Python objects.\n\n&lt;br&gt;
&lt;br&gt;
&lt;code&gt;python\nfrom orm_models import JobDescription, Session\n\ndef get_job_descriptions_by_company_orm(company_id: str):\n    """\n    Retrieve JobDescriptions for a given company using ORM.\n    Returns a list of JobDescription objects.\n    """\n    session = Session()\n    try:\n        results = session.query(JobDescription)\n                         .filter(JobDescription.company_id == company_id)\n                         .all()\n        return results\n    finally:\n        session.close()\n\n# Example usage:\nif __name__ == '__main__':\n    jobs = get_job_descriptions_by_company_orm('COMPANY_123')\n    for job in jobs:\n        print(f"{job.company_name}: {job.job_description}")\n&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
\n\n## Advantages of the ORM Approach\n- &lt;strong&gt;Abstraction &amp;amp; Simplicity&lt;/strong&gt;: You deal with Python objects; the ORM hides the SQL details.\n- &lt;strong&gt;Maintainability&lt;/strong&gt;: Changes to models update all the underlying queries.\n- &lt;strong&gt;Safety &amp;amp; Consistency&lt;/strong&gt;: Automatic parameter binding prevents SQL injection.\n- &lt;strong&gt;Relationship Handling&lt;/strong&gt;: ORMs make it easier to navigate relationships among models.\n\n## Disadvantages of the ORM Approach\n- &lt;strong&gt;Abstraction Overhead&lt;/strong&gt;: Sometimes it can be less efficient if you need very fine-tuned queries.\n- &lt;strong&gt;Complexity for Advanced Queries&lt;/strong&gt;: Very complex queries or performance optimizations may require custom SQL.\n\n# Bit-Out-ORM Layer: Repository Using Raw SQL\nIn contrast, this repository function uses raw SQL queries via SQLAlchemy’s connection API.\n\n&lt;br&gt;
&lt;br&gt;
&lt;code&gt;python\nfrom orm_models import engine\n\ndef get_job_descriptions_by_company_raw(company_id: str):\n    """\n    Retrieve job descriptions for a given company using raw SQL.\n    Returns a list of RowProxy objects.\n    """\n    with engine.connect() as connection:\n        # Use parameterized queries to avoid SQL injection\n        result = connection.execute(\n            "SELECT id, company_id, company_name, job_description FROM job_description WHERE company_id = :company_id",\n            {"company_id": company_id}\n        )\n        return result.fetchall()\n\n# Example usage:\nif __name__ == '__main__':\n    rows = get_job_descriptions_by_company_raw('COMPANY_123')\n    for row in rows:\n        print(f"{row['company_name']}: {row['job_description']}")\n&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
\n\n## Advantages of the Raw SQL Approach\n- &lt;strong&gt;Fine-Grained Control&lt;/strong&gt;: Directly write and optimize SQL queries for complex or performance-critical tasks.\n- &lt;strong&gt;Transparency&lt;/strong&gt;: You see exactly what SQL is sent to the database.\n- &lt;strong&gt;Flexibility&lt;/strong&gt;: Use database-specific features or functions not directly exposed by the ORM.\n\n## Disadvantages of the Raw SQL Approach\n- &lt;strong&gt;Manual Mapping&lt;/strong&gt;: You must manually convert results to Python objects if needed.\n- &lt;strong&gt;Error-Prone&lt;/strong&gt;: Writing raw SQL can lead to mistakes, and maintaining it over time may be more challenging.\n- &lt;strong&gt;Repetition&lt;/strong&gt;: Common CRUD operations might require writing repetitive SQL queries when an ORM could generate them automatically.\n\n# Summary and Comparison\nIn a full-scale system, it’s common to use an ORM for most database operations while falling back on raw SQL for advanced use cases that require fine-tuning. This hybrid approach allows you to benefit from high productivity when possible while retaining control when needed.&lt;/p&gt;

</description>
      <category>python</category>
      <category>database</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Did you know you can optimize database access with ORM and raw SQL?</title>
      <dc:creator>Negitama</dc:creator>
      <pubDate>Sat, 14 Jun 2025 14:21:45 +0000</pubDate>
      <link>https://dev.to/negitamaai/did-you-know-you-can-optimize-database-access-with-orm-and-raw-sql-59hg</link>
      <guid>https://dev.to/negitamaai/did-you-know-you-can-optimize-database-access-with-orm-and-raw-sql-59hg</guid>
      <description>&lt;p&gt;Did you know that you can choose between using SQLAlchemy ORM and raw SQL queries for database interactions in Python? Let's explore how each approach could benefit your development.&lt;/p&gt;

&lt;h1&gt;
  
  
  Setup: Define Models with SQLAlchemy ORM
&lt;/h1&gt;

&lt;p&gt;First, we create our ORM models. This file (e.g., orm_models.py) defines the schema in code using SQLAlchemy’s declarative base.&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;sqlalchemy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;String&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="n"&gt;create_engine&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy.ext.declarative&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;declarative_base&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy.orm&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sessionmaker&lt;/span&gt;

&lt;span class="n"&gt;Base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;declarative_base&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;JobDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;__tablename__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;job_description&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;primary_key&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;company_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;company_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&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="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;job_description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&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="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CandidateResume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;__tablename__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;candidate_resume&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;primary_key&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;candidate_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;candidate_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&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="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;resume&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&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="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;postgresql://user:password@localhost/dbname&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;Session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sessionmaker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Bit-ORM Layer: Repository Using the ORM
&lt;/h1&gt;

&lt;p&gt;This repository function uses the ORM—meaning we work with Python objects. The ORM layer automatically converts our model objects into the appropriate SQL queries. Save this in (e.g.) repository_orm.py.&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;orm_models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;JobDescription&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_job_descriptions_by_company_orm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;company_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Session&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;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JobDescription&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;\
                         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JobDescription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;company_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;company_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;\
                         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&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;results&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;jobs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_job_descriptions_by_company_orm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;COMPANY_123&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;job&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;jobs&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;company_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;job_description&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Advantages of the ORM Approach&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Abstraction &amp;amp; Simplicity: You deal with Python objects; the ORM hides the SQL details.&lt;/li&gt;
&lt;li&gt;Maintainability: Changes to models update all the underlying queries.&lt;/li&gt;
&lt;li&gt;Safety &amp;amp; Consistency: Automatic parameter binding prevents SQL injection.&lt;/li&gt;
&lt;li&gt;Relationship Handling: ORMs make it easier to navigate relationships among models.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disadvantages of the ORM Approach&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Abstraction Overhead: Sometimes it can be less efficient if you need very fine-tuned queries.&lt;/li&gt;
&lt;li&gt;Complexity for Advanced Queries: Very complex queries or performance optimizations may require custom SQL.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Bit-Out-ORM Layer: Repository Using Raw SQL
&lt;/h1&gt;

&lt;p&gt;In contrast, this repository function uses raw SQL queries via SQLAlchemy’s connection API. Save this as repository_raw.py.&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;orm_models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_job_descriptions_by_company_raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;company_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT id, company_id, company_name, job_description FROM job_description WHERE company_id = :company_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;company_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;company_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_job_descriptions_by_company_raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;COMPANY_123&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;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;company_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;job_description&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Advantages of the Raw SQL Approach&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fine-Grained Control: Directly write and optimize SQL queries for complex or performance-critical tasks.&lt;/li&gt;
&lt;li&gt;Transparency: You see exactly what SQL is sent to the database.&lt;/li&gt;
&lt;li&gt;Flexibility: Use database-specific features or functions not directly exposed by the ORM.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disadvantages of the Raw SQL Approach&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manual Mapping: You must manually convert results to Python objects if needed.&lt;/li&gt;
&lt;li&gt;Error-Prone: Writing raw SQL can lead to mistakes, and maintaining it over time may be more challenging.&lt;/li&gt;
&lt;li&gt;Repetition: Common CRUD operations might require writing repetitive SQL queries when an ORM could generate them automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Summary and Comparison
&lt;/h1&gt;

&lt;p&gt;In a full-scale system, it’s common to use an ORM for most database operations while falling back on raw SQL for advanced use cases that require fine-tuning. This hybrid approach allows you to benefit from high productivity when possible while retaining control when needed.&lt;/p&gt;

</description>
      <category>python</category>
      <category>database</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Two Approaches to Database Interaction with SQLAlchemy in Python</title>
      <dc:creator>Negitama</dc:creator>
      <pubDate>Sat, 14 Jun 2025 14:13:48 +0000</pubDate>
      <link>https://dev.to/negitamaai/two-approaches-to-database-interaction-with-sqlalchemy-in-python-538n</link>
      <guid>https://dev.to/negitamaai/two-approaches-to-database-interaction-with-sqlalchemy-in-python-538n</guid>
      <description>&lt;p&gt;Ever wondered about the best way to work with databases in Python? In this post, I've explored two different approaches using SQLAlchemy: the ORM layer and using raw SQL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Setup: Define Models with SQLAlchemy ORM&lt;/strong&gt;&lt;br&gt;
First, we define our ORM models in a file (e.g., orm_models.py). These models define the database schema in code using SQLAlchemy’s declarative base. We create a &lt;code&gt;JobDescription&lt;/code&gt; model and a &lt;code&gt;CandidateResume&lt;/code&gt; model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Bit-ORM Layer: Repository Using the ORM&lt;/strong&gt;&lt;br&gt;
This approach uses the ORM to automatically convert model objects into SQL queries. Check out the code in repository_orm.py, where we retrieve job descriptions using the ORM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages of the ORM Approach&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Abstraction &amp;amp; Simplicity&lt;/strong&gt;: Deal with Python objects and let the ORM handle SQL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainability and Safety&lt;/strong&gt;: Model changes update underlying queries and prevent SQL injection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Bit-Out-ORM Layer: Repository Using Raw SQL&lt;/strong&gt;&lt;br&gt;
Contrast this with using raw SQL in repository_raw.py. This approach allows for direct query writing with potential optimizations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages of the Raw SQL Approach&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fine-Grained Control&lt;/strong&gt;: Write optimized SQL for complex tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transparency &amp;amp; Flexibility&lt;/strong&gt;: See the exact SQL sent and use database-specific features.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Summary and Comparison&lt;/strong&gt;&lt;br&gt;
Consider using an ORM for regular tasks and raw SQL for fine-tuned, performance-critical queries. This hybrid approach provides productivity with control when needed.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>python</category>
      <category>database</category>
    </item>
    <item>
      <title>SpannerDB Reading Capabilities and Transaction Specifications</title>
      <dc:creator>Negitama</dc:creator>
      <pubDate>Sat, 14 Jun 2025 14:11:50 +0000</pubDate>
      <link>https://dev.to/negitamaai/spannerdb-reading-capabilities-and-transaction-specifications-130g</link>
      <guid>https://dev.to/negitamaai/spannerdb-reading-capabilities-and-transaction-specifications-130g</guid>
      <description>&lt;p&gt;SpannerDB Reading Capabilities and Transaction Specifications\n\nSpannerDB (Google Cloud Spanner) provides sophisticated reading capabilities designed to balance consistency, performance, and global scale. This report explores the comprehensive read transaction specifications of SpannerDB, examining its various transaction types, consistency guarantees, and implementation details.\n\n## Core Read Transaction Types\n\nGoogle Spanner offers several distinct reading capabilities that serve different application needs while maintaining strong consistency guarantees across a globally distributed database.&lt;/p&gt;

</description>
      <category>database</category>
      <category>cloud</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Integrating BM25 in Hybrid Search and Reranking Pipelines: Strategies and Applications</title>
      <dc:creator>Negitama</dc:creator>
      <pubDate>Sat, 14 Jun 2025 14:07:00 +0000</pubDate>
      <link>https://dev.to/negitamaai/integrating-bm25-in-hybrid-search-and-reranking-pipelines-strategies-and-applications-2f7b</link>
      <guid>https://dev.to/negitamaai/integrating-bm25-in-hybrid-search-and-reranking-pipelines-strategies-and-applications-2f7b</guid>
      <description>&lt;p&gt;Integrating BM25 in Hybrid Search and Reranking Pipelines: Strategies and Applications\n\nBM25 (Best Matching 25) is a foundational algorithm in information retrieval, renowned for its efficiency in keyword-based relevance scoring. While modern neural rerankers and vector search dominate advanced retrieval systems, BM25 remains a critical component in hybrid architectures and reranking workflows. This report examines BM25’s dual role in hybrid search systems and reranking pipelines, analyzing implementation patterns, use cases, and technical considerations.\n\n## 1. BM25 as a Hybrid Search Component\nHybrid search combines keyword-based retrieval (BM25) with semantic vector search to balance precision and recall. BM25’s role here is to ensure exact keyword matches and term rarity are prioritized, while vector search captures contextual relationships.\n\n### 1.1 Parallel Retrieval Fusion\nIn systems like Elasticsearch and Weaviate, BM25 and vector search run independently, with results merged using fusion algorithms:\n\n- &lt;strong&gt;Reciprocal Rank Fusion (RRF):&lt;/strong&gt; Combines rankings from both methods using the formula: $$RRF_score = \sum_{i} \frac{1}{k + rank_i}$$\n- &lt;strong&gt;Weighted Score Combination:&lt;/strong&gt; Assigns tunable weights $\alpha$ to BM25 and vector similarity scores: $$Final_score = \alpha \cdot BM25_score + (1-\alpha) \cdot Vector_score$$\n\n### 1.2 BM25 as a Pre-Filter\nIn latency-sensitive applications, BM25 narrows the candidate pool before vector search:\n&lt;br&gt;
&lt;br&gt;
&lt;code&gt;sql\nSELECT * FROM documents \nWHERE bm25_match(query) \nORDER BY vector_similarity DESC LIMIT 100\n&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
\nThis two-stage retrieval reduces computational overhead by excluding irrelevant documents early.\n\n### 1.3 BM25F for Field-Aware Hybrid Search\nBM25F extends BM25 to weight fields differently (e.g., title vs. body). Weaviate implements this for structured data:\n$$BM25F_score = \sum_{fields} w_f \cdot \frac{TF_f}{k_1 (1-b + b \cdot \frac{DL_f}{avgDL_f}) + TF_f} \cdot IDF$$\nwhere $w_f$ is the field weight, $DL_f$ is the field length, and $b$ controls length normalization.\n\n## 2. BM25 in Reranking Pipelines\nWhile BM25 is not a standalone neural reranker, it enhances reranking through score fusion, feature engineering, and fallback mechanisms.\n\n### 2.1 Hybrid Pre-Reranking\nBM25 and vector search retrieve 100–200 candidates, which are then processed by cross-encoders (e.g., bge-reranker-v2-m3) or LLMs:\n- BM25 retrieves 50 documents.\n- Vector search retrieves 50 documents.\n- A cross-encoder reranks the combined 100 documents.\n\n### 2.2 Score Augmentation for Neural Rerankers\nBM25 scores are injected as features into reranking models:\n&lt;br&gt;
&lt;br&gt;
&lt;code&gt;json\n{"document": "text", "bm25_score": 0.85, "vector_score": 0.92}\n&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
\nThe TREC Deep Learning Track shows that appending BM25 scores as text tokens (e.g., "BM25=0.85") improves BERT-based reranker accuracy by 7.3% &lt;a href="mailto:MRR@10"&gt;MRR@10&lt;/a&gt;.\n\n### 2.3 Fallback Tiebreaking\nWhen neural rerankers produce tied scores, BM25 breaks ties:\n&lt;br&gt;
&lt;br&gt;
&lt;code&gt;python\nsorted_results = sorted(\n    tied_results, \n    key=lambda x: (x['rerank_score'], x['bm25_score'])\n)\n&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
\nThis is critical in legal or regulatory contexts where explainability matters.\n\n## 3. Use Cases and Implementation Guidance\n\n## 3.1 When to Use BM25 in Hybrid/Reranking\n\n## 3.2 Optimization Strategies\n- &lt;strong&gt;Parameter Tuning:&lt;/strong&gt; Adjust $k_1$ (term frequency saturation) and $b$ (length normalization) based on document length variance. For technical documents, $k_1=1.2$, $b=0.75$ often works best.\n- &lt;strong&gt;Dynamic Weighting:&lt;/strong&gt; Use query classification to set $\alpha$ in hybrid scores. For navigational queries (e.g., "Facebook login"), $\alpha=0.8$; for exploratory queries (e.g., "AI ethics"), $\alpha=0.3$.\n- &lt;strong&gt;BM25-Driven Pruning:&lt;/strong&gt; Exclude documents with BM25 scores below a threshold (e.g., $BM25 &amp;lt; 1.5$) before vector search to reduce latency.\n\n## 4. Limitations and Alternatives\n\n### 4.1 BM25 Shortcomings\n- Fails to capture semantic relationships (e.g., synonymy: "car" vs. "automobile").\n- Struggles with long-tail queries in low-resource languages.\n- Scores are not directly comparable across indexes, complicating federated search.\n\n### 4.2 When to Use Neural Rerankers Instead\n- &lt;strong&gt;High semantic complexity:&lt;/strong&gt; Queries like "impact of inflation on renewable energy adoption" benefit from cross-encoders.\n- &lt;strong&gt;Multilingual settings:&lt;/strong&gt; Models like Cohere Rerank or Vectara Multilingual outperform BM25 in 40+ languages.\n- &lt;strong&gt;Personalization:&lt;/strong&gt; User-specific reranking requires learning-to-rank (LTR) models.\n\n## 5. Emerging Trends\n\n- &lt;strong&gt;BM25 as a Reranker Feature:&lt;/strong&gt; The TREC 2023 Deep Learning Track found that concatenating BM25 scores to document text (e.g., "Document: ... [BM25=0.72]") improves reranker robustness.\n- &lt;strong&gt;Sparse-Dense Hybrids:&lt;/strong&gt; SPLADE (Sparse Lexical and Dense) models unify BM25-like term weights with neural representations, achieving 94% of BM25’s speed with 98% of BERT’s accuracy.\n- &lt;strong&gt;BM25 in LLM Pipelines:&lt;/strong&gt; LangChain and LlamaIndex use BM25 to filter context for LLMs, reducing hallucination risks by 22–37%.\n\n## Conclusion\nBM25 remains indispensable in hybrid and reranking systems despite the rise of neural methods. Its strengths—computational efficiency, explainability, and exact-match precision—complement vector search’s semantic understanding. Implementations range from simple score fusion to complex feature engineering in cross-encoders. For optimal results:\n\n- Use BM25 as a first-stage retriever in hybrid pipelines.\n- Integrate its scores into neural rerankers via feature injection.\n- Reserve pure neural reranking for high-resource, semantically complex scenarios.\n\nThis dual role ensures BM25’s continued relevance in an era dominated by large language models and semantic search technologies.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Integrating BM25 in Hybrid Search and Reranking Pipelines: Strategies and Applications</title>
      <dc:creator>Negitama</dc:creator>
      <pubDate>Sat, 14 Jun 2025 10:19:34 +0000</pubDate>
      <link>https://dev.to/negitamaai/integrating-bm25-in-hybrid-search-and-reranking-pipelines-strategies-and-applications-4joi</link>
      <guid>https://dev.to/negitamaai/integrating-bm25-in-hybrid-search-and-reranking-pipelines-strategies-and-applications-4joi</guid>
      <description>&lt;h1&gt;
  
  
  Integrating BM25 in Hybrid Search and Reranking Pipelines: Strategies and Applications
&lt;/h1&gt;

&lt;p&gt;BM25 (Best Matching 25) is a foundational algorithm in information retrieval, renowned for its efficiency in keyword-based relevance scoring. While modern neural rerankers and vector search dominate advanced retrieval systems, BM25 remains a critical component in hybrid architectures and reranking workflows. This report examines BM25’s dual role in hybrid search systems and reranking pipelines, analyzing implementation patterns, use cases, and technical considerations.&lt;/p&gt;

&lt;h2&gt;
  
  
  BM25 as a Hybrid Search Component
&lt;/h2&gt;

&lt;p&gt;Hybrid search combines keyword-based retrieval (BM25) with semantic vector search to balance precision and recall. BM25’s role here is to ensure exact keyword matches and term rarity are prioritized, while vector search captures contextual relationships.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parallel Retrieval Fusion
&lt;/h3&gt;

&lt;p&gt;In systems like Elasticsearch and Weaviate, BM25 and vector search run independently, with results merged using fusion algorithms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reciprocal Rank Fusion (RRF)&lt;/strong&gt;: Combines rankings from both methods using the formula: RRF_score=sum\dfrac{1}{k+rank_position}&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weighted Score Combination&lt;/strong&gt;: Assigns tunable weights (α\alphaα) to BM25 and vector similarity scores: Final_score=α⋅BM25_score+(1−α)⋅Vector_score&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  BM25 as a Pre-Filter
&lt;/h3&gt;

&lt;p&gt;In latency-sensitive applications, BM25 narrows the candidate pool before vector search:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;bm25&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_similarity&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This two-stage retrieval reduces computational overhead by excluding irrelevant documents early.&lt;/p&gt;

&lt;h3&gt;
  
  
  BM25F for Field-Aware Hybrid Search
&lt;/h3&gt;

&lt;p&gt;BM25F extends BM25 to weight fields differently (e.g., title vs. body). Weaviate implements this for structured data:&lt;br&gt;
BM25F_score=sum_{fields} wf⋅TFf / (k1 ( (1−b+b⋅DLf/avgDLf) + TFf ) ⋅ IDF&lt;br&gt;
where wf is the field weight, DLf is the field length, and b controls length normalization.&lt;/p&gt;
&lt;h2&gt;
  
  
  BM25 in Reranking Pipelines
&lt;/h2&gt;

&lt;p&gt;While BM25 is not a standalone neural reranker, it enhances reranking through score fusion, feature engineering, and fallback mechanisms.&lt;/p&gt;
&lt;h3&gt;
  
  
  Hybrid Pre-Reranking
&lt;/h3&gt;

&lt;p&gt;BM25 and vector search retrieve 100–200 candidates, which are then processed by cross-encoders or LLMs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;BM25 retrieves 50 documents.&lt;/li&gt;
&lt;li&gt;Vector search retrieves 50 documents.&lt;/li&gt;
&lt;li&gt;A cross-encoder reranks the combined 100 documents.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Score Augmentation for Neural Rerankers
&lt;/h3&gt;

&lt;p&gt;BM25 scores are injected as features into reranking models:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"document"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
  &lt;/span&gt;&lt;span class="nl"&gt;"bm25_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
  &lt;/span&gt;&lt;span class="nl"&gt;"vector_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.92&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The TREC Deep Learning Track shows that appending BM25 scores as text tokens (e.g., "BM25=0.85") improves BERT-based reranker accuracy by 7.3% MRR@10.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fallback Tiebreaking
&lt;/h3&gt;

&lt;p&gt;When neural rerankers produce tied scores, BM25 breaks ties:&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;sorted_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;tied_results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rerank_score&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="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bm25_score&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is critical in legal or regulatory contexts where explainability matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Cases and Implementation Guidance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  When to Use BM25 in Hybrid/Reranking
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Optimization Strategies
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Parameter Tuning&lt;/strong&gt;: Adjust k1 (term frequency saturation) and b (length normalization) based on document length variance. For technical documents, k1=1.2, b=0.75 often works best.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Weighting&lt;/strong&gt;: Use query classification to set α in hybrid scores. For navigational queries (e.g., "Facebook login"), α=0.8; for exploratory queries (e.g., "AI ethics"), α=0.3.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BM25-Driven Pruning&lt;/strong&gt;: Exclude documents with BM25 scores below a threshold (e.g., BM25 &amp;lt; 1.5) before vector search to reduce latency.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Limitations and Alternatives
&lt;/h2&gt;

&lt;h3&gt;
  
  
  BM25 Shortcomings
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Fails to capture semantic relationships (e.g., synonymy: "car" vs. "automobile").&lt;/li&gt;
&lt;li&gt;Struggles with long-tail queries in low-resource languages.&lt;/li&gt;
&lt;li&gt;Scores are not directly comparable across indexes, complicating federated search.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When to Use Neural Rerankers Instead
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;High semantic complexity: Queries like "impact of inflation on renewable energy adoption" benefit from cross-encoders.&lt;/li&gt;
&lt;li&gt;Multilingual settings: Models like Cohere Rerank or Vectara Multilingual outperform BM25 in 40+ languages.&lt;/li&gt;
&lt;li&gt;Personalization: User-specific reranking requires learning-to-rank models.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Emerging Trends
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;BM25 as a Reranker Feature&lt;/strong&gt;: The TREC 2023 Deep Learning Track found that concatenating BM25 scores to document text (e.g., "Document: ... [BM25=0.72]") improves reranker robustness.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sparse-Dense Hybrids&lt;/strong&gt;: SPLADE models unify BM25-like term weights with neural representations, achieving 94% of BM25’s speed with 98% of BERT’s accuracy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BM25 in LLM Pipelines&lt;/strong&gt;: LangChain and LlamaIndex use BM25 to filter context for LLMs, reducing hallucination risks by 22–37%.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;BM25 remains indispensable in hybrid and reranking systems despite the rise of neural methods. Its strengths—computational efficiency, explainability, and exact-match precision—complement vector search’s semantic understanding. Implementations range from simple score fusion to complex feature engineering in cross-encoders. For optimal results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use BM25 as a first-stage retriever in hybrid pipelines.&lt;/li&gt;
&lt;li&gt;Integrate its scores into neural rerankers via feature injection.&lt;/li&gt;
&lt;li&gt;Reserve pure neural reranking for high-resource, semantically complex scenarios.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This dual role ensures BM25’s continued relevance in an era dominated by large language models and semantic search technologies.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Namespace vs Regular Packages in Python — And Why mypy Might Be Failing You</title>
      <dc:creator>Negitama</dc:creator>
      <pubDate>Fri, 13 Jun 2025 16:41:30 +0000</pubDate>
      <link>https://dev.to/negitamaai/namespace-vs-regular-packages-in-python-and-why-mypy-might-be-failing-you-3jnk</link>
      <guid>https://dev.to/negitamaai/namespace-vs-regular-packages-in-python-and-why-mypy-might-be-failing-you-3jnk</guid>
      <description>&lt;p&gt;If you're building AI systems, data pipelines, or backend services in Python, you’ve probably run into weird bugs with mypy not picking up types or imports mysteriously failing—especially when you’re working across microservices or large codebases. Chances are… you’re using a namespace package (maybe without even knowing it). Let’s break it down.&lt;/p&gt;

&lt;p&gt;Regular Packages vs Namespace Packages&lt;/p&gt;

&lt;p&gt;Regular Packages&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Require an &lt;strong&gt;init&lt;/strong&gt;.py file&lt;/li&gt;
&lt;li&gt;Define a single, self-contained folder&lt;/li&gt;
&lt;li&gt;Easy for mypy, IDEs, linters, and tests to process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example of a Regular package:&lt;br&gt;
project/&lt;br&gt;
└── analytics/&lt;br&gt;
    ├── &lt;strong&gt;init&lt;/strong&gt;.py&lt;br&gt;
    ├── metrics.py&lt;br&gt;
    └── models.py&lt;/p&gt;

&lt;p&gt;Namespace Packages&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No &lt;strong&gt;init&lt;/strong&gt;.py needed&lt;/li&gt;
&lt;li&gt;Split across multiple folders/repos&lt;/li&gt;
&lt;li&gt;Used in plugin systems or modular AI/ML tooling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example of a Namespace package:&lt;br&gt;
src/coretools/featurestore/&lt;br&gt;
    encoder.py&lt;br&gt;
libs/featurestore/&lt;br&gt;
    scaler.py&lt;/p&gt;

&lt;p&gt;With namespace packages, coretools.featurestore.encoder and libs.featurestore.scaler can coexist under the same import path.&lt;/p&gt;

&lt;p&gt;Great for scalability. Nightmare for static analysis—unless configured right.&lt;/p&gt;

&lt;p&gt;Why AI Devs &amp;amp; Data Teams Should Care&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modular Pipelines: When your training logic and feature store live in different repos&lt;/li&gt;
&lt;li&gt;Plugin Systems: For experiment tracking, custom metrics, preprocessing layers&lt;/li&gt;
&lt;li&gt;Shared AI Tooling: Across internal libraries, you may already be “namespacing” without realizing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But here's the catch…&lt;/p&gt;

&lt;p&gt;Why mypy Can Break on Namespace Packages&lt;/p&gt;

&lt;p&gt;Your Developer Checklist to Fix mypy with Namespace Packages&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enable namespace support
mypy --namespace-packages&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Or in mypy.ini:&lt;br&gt;
   [mypy]&lt;br&gt;
   namespace_packages = true&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Use p your.package.name instead of just the folder&lt;br&gt;
mypy -p featurestore.encoder&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set MYPYPATH + --explicit-package-bases if your source layout is non-standard&lt;br&gt;
export MYPYPATH=src&lt;br&gt;
mypy --explicit-package-bases -p yourpkg.module&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Still struggling? Add dummy &lt;strong&gt;init&lt;/strong&gt;.pyi or &lt;strong&gt;init&lt;/strong&gt;.py&lt;br&gt;
This helps tools infer structure even in namespace packages.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Summary Table&lt;br&gt;
“Namespaces are one honking great idea — let's do more of those.”&lt;/p&gt;

&lt;p&gt;TakeAway&lt;br&gt;
If you're building modular AI pipelines, ML services, or shared tooling across teams—you need to understand how namespace packages and tools like mypy interact. It's the difference between silent bugs and confident code.&lt;/p&gt;

&lt;p&gt;Have you hit these issues in production or CI? Let’s compare notes.&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>mypy</category>
      <category>namespacepackages</category>
    </item>
    <item>
      <title>Namespace vs Regular Packages in Python — And Why mypy Might Be Failing You</title>
      <dc:creator>Negitama</dc:creator>
      <pubDate>Fri, 13 Jun 2025 16:36:04 +0000</pubDate>
      <link>https://dev.to/negitamaai/namespace-vs-regular-packages-in-python-and-why-mypy-might-be-failing-you-22fg</link>
      <guid>https://dev.to/negitamaai/namespace-vs-regular-packages-in-python-and-why-mypy-might-be-failing-you-22fg</guid>
      <description>&lt;p&gt;If you're building AI systems, data pipelines, or backend services in Python, you’ve probably run into weird bugs with mypy not picking up types or imports mysteriously failing—especially when you’re working across microservices or large codebases. Chances are… you’re using a namespace package (maybe without even knowing it). Let’s break it down.&lt;br&gt;
📦 Regular Packages vs Namespace Packages&lt;/p&gt;

&lt;p&gt;Regular Packages&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Require an &lt;strong&gt;init&lt;/strong&gt;.py file&lt;/li&gt;
&lt;li&gt;Define a single, self-contained folder&lt;/li&gt;
&lt;li&gt;Easy for mypy, IDEs, linters, and tests to process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Namespace Packages&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No &lt;strong&gt;init&lt;/strong&gt;.py needed&lt;/li&gt;
&lt;li&gt;Split across multiple folders/repos&lt;/li&gt;
&lt;li&gt;Used in plugin systems or modular AI/ML tooling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With namespace packages, coretools.featurestore.encoder and libs.featurestore.scaler can coexist under the same import path.&lt;br&gt;
👉 Great for scalability. Nightmare for static analysis—unless configured right.&lt;/p&gt;

&lt;p&gt;🧪 Why AI Devs &amp;amp; Data Teams Should Care&lt;br&gt;
🔌 Modular Pipelines&lt;br&gt;
🧩 Plugin Systems&lt;br&gt;
🧠 Shared AI Tooling&lt;/p&gt;

&lt;p&gt;But here's the catch…&lt;/p&gt;

&lt;p&gt;🚨 Why mypy Can Break on Namespace Packages&lt;/p&gt;

&lt;p&gt;✅ Your Developer Checklist to Fix mypy with Namespace Packages&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable namespace support&lt;/li&gt;
&lt;li&gt;Use your.package.name&lt;/li&gt;
&lt;li&gt;Set MYPYPATH + -explicit-package-bases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Still struggling? Add dummy &lt;strong&gt;init&lt;/strong&gt;.pyi or &lt;strong&gt;init&lt;/strong&gt;.py&lt;/p&gt;

&lt;p&gt;🧾 Summary Table&lt;/p&gt;

&lt;p&gt;🔍 TakeAway&lt;br&gt;
If you're building modular AI pipelines, ML services, or shared tooling across teams—you need to understand how namespace packages and tools like mypy interact. It's the difference between silent bugs and confident code.&lt;/p&gt;

&lt;p&gt;Have you hit these issues in production or CI? Let’s compare notes.&lt;/p&gt;

</description>
      <category>python</category>
      <category>aiengineering</category>
      <category>mlops</category>
      <category>mypy</category>
    </item>
    <item>
      <title>Namespace vs Regular Packages in Python — And Why mypy Might Be Failing You</title>
      <dc:creator>Negitama</dc:creator>
      <pubDate>Fri, 13 Jun 2025 16:28:42 +0000</pubDate>
      <link>https://dev.to/negitamaai/namespace-vs-regular-packages-in-python-and-why-mypy-might-be-failing-you-7fn</link>
      <guid>https://dev.to/negitamaai/namespace-vs-regular-packages-in-python-and-why-mypy-might-be-failing-you-7fn</guid>
      <description>&lt;p&gt;Namespace vs Regular Packages in Python — And Why mypy Might Be Failing You&lt;br&gt;
If you're building AI systems, data pipelines, or backend services in Python, you’ve probably run into weird bugs with mypy not picking up types or imports mysteriously failing—especially when you’re working across microservices or large codebases.&lt;br&gt;
Chances are… you’re using a namespace package (maybe without even knowing it). Let’s break it down.&lt;/p&gt;

&lt;p&gt;Regular Packages vs Namespace Packages&lt;br&gt;
Regular Packages&lt;br&gt;
Require an &lt;strong&gt;init&lt;/strong&gt;.py file&lt;br&gt;
Define a single, self-contained folder&lt;br&gt;
Easy for mypy, IDEs, linters, and tests to process&lt;/p&gt;

&lt;h1&gt;
  
  
  Regular package
&lt;/h1&gt;

&lt;p&gt;project/&lt;br&gt;
└── analytics/&lt;br&gt;
    ├── &lt;strong&gt;init&lt;/strong&gt;.py&lt;br&gt;
    ├── metrics.py&lt;br&gt;
    └── models.py&lt;br&gt;
Namespace Packages&lt;br&gt;
No &lt;strong&gt;init&lt;/strong&gt;.py needed&lt;br&gt;
Split across multiple folders/repos&lt;br&gt;
Used in plugin systems or modular AI/ML tooling&lt;/p&gt;

&lt;h1&gt;
  
  
  Namespace package
&lt;/h1&gt;

&lt;p&gt;src/coretools/featurestore/&lt;br&gt;
    encoder.py&lt;br&gt;
libs/featurestore/&lt;br&gt;
    scaler.py&lt;/p&gt;

&lt;p&gt;With namespace packages, coretools.featurestore.encoder and libs.featurestore.scaler can coexist under the same import path.&lt;br&gt;
Great for scalability. Nightmare for static analysis—unless configured right.&lt;/p&gt;

&lt;p&gt;Why AI Devs &amp;amp; Data Teams Should Care&lt;br&gt;
Modular Pipelines: When your training logic and feature store live in different repos&lt;br&gt;
Plugin Systems: For experiment tracking, custom metrics, preprocessing layers&lt;br&gt;
Shared AI Tooling: Across internal libraries, you may already be “namespacing” without realizing&lt;br&gt;
But here's the catch…&lt;/p&gt;

&lt;p&gt;Why mypy Can Break on Namespace Packages&lt;br&gt;
Your Developer Checklist to Fix mypy with Namespace Packages&lt;br&gt;
Enable namespace support&lt;br&gt;
mypy --namespace-packages&lt;br&gt;
Or in mypy.ini:&lt;br&gt;
[mypy]&lt;br&gt;
namespace_packages = true&lt;/p&gt;

&lt;p&gt;Use p your.package.name instead of just the folder&lt;br&gt;
mypy -p featurestore.encoder&lt;/p&gt;

&lt;p&gt;Set MYPYPATH + -explicit-package-bases if your source layout is non-standard&lt;br&gt;
export MYPYPATH=src&lt;br&gt;
mypy --explicit-package-bases -p yourpkg.module&lt;/p&gt;

&lt;p&gt;Still struggling? Add dummy &lt;strong&gt;init&lt;/strong&gt;.pyi or &lt;strong&gt;init&lt;/strong&gt;.py&lt;br&gt;
This helps tools infer structure even in namespace packages.&lt;br&gt;
Summary Table&lt;br&gt;
“Namespaces are one honking great idea — let's do more of those.”&lt;/p&gt;

&lt;p&gt;TakeAway&lt;br&gt;
If you're building modular AI pipelines, ML services, or shared tooling across teams—you need to understand how namespace packages and tools like mypy interact. It's the difference between silent bugs and confident code.&lt;br&gt;
Have you hit these issues in production or CI? Let’s compare notes.&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>data</category>
      <category>mypy</category>
    </item>
  </channel>
</rss>
