DEV Community

Rahul Singh
Rahul Singh

Posted on • Originally published at aicodereview.cc

Semgrep vs Pylint: Security Analysis vs Code Quality (2026)

Quick Verdict

Semgrep screenshot

Semgrep and Pylint both analyze Python code statically, but they solve different problems and belong to different categories of tooling. Pylint is a Python code quality linter that checks for bugs, style violations, code complexity, unused variables, and adherence to coding standards. Semgrep is a security-focused SAST (Static Application Security Testing) engine that detects vulnerabilities, performs cross-file taint analysis, and lets teams write custom security rules in YAML.

Comparing them directly is a bit like comparing a spell-checker to a structural engineer's analysis - both inspect the same artifact, but for entirely different properties and with entirely different methods. The reason this comparison comes up frequently is that Python developers searching for "static analysis" or "code analysis" tools encounter both, and the distinction between code quality linting and security scanning is not always obvious from tool descriptions.

Choose Pylint if: you need comprehensive Python code quality enforcement - style checks, complexity analysis, dead code detection, naming convention enforcement, unused variable detection, and deep Python semantic analysis. Pylint (or the faster Ruff as an alternative) belongs in every Python project's development workflow.

Choose Semgrep if: you need to scan Python code for security vulnerabilities - SQL injection, command injection, XSS, insecure dependencies, hardcoded secrets, weak cryptography, and framework-specific security issues. Semgrep's security rule library and cross-file taint analysis make it the appropriate tool for application security.

The right answer for most Python teams: Run both. They are not competing tools - they analyze different dimensions of code quality and should both be present in a mature Python development toolchain. This post explains what each tool does, where they overlap, and how to configure them together effectively.

At-a-Glance Comparison

Category Semgrep Pylint
Primary purpose Security SAST / vulnerability detection Code quality linting
Languages 30+ (Python, JS, TS, Java, Go, Ruby, C, C++, etc.) Python only
Security rules 2,800+ community / 20,000+ Pro Minimal - not a security tool
Code quality rules Limited community rules 400+ built-in message categories
Custom rules Yes - YAML, mirrors target language syntax Yes - Python checker plugins
Cross-file analysis Yes (Pro engine, paid tier) Yes - basic cross-module awareness
Taint tracking Yes (Pro engine) No
Complexity analysis No Yes - cyclomatic complexity, Halstead
Style enforcement No Yes - PEP 8, naming, docstrings
Unused variable detection No Yes
Framework-aware security Yes - Django, Flask, FastAPI Via plugins (pylint-django, etc.)
AI triage Yes - Semgrep Assistant (paid) No
IDE integration VS Code extension Wide support via language servers
Pre-commit support Yes Yes
CI/CD integration Native GitHub Actions support pip install, standard exit codes
Pricing OSS CLI free; Team tier free for 10 contributors Completely free, always
Paid pricing $35/contributor/month (Team tier) No paid tier
License LGPL-2.1 (OSS) / commercial (Pro) GPL-2.0
SCA / dependency scanning Yes - Semgrep Supply Chain (Pro) No
Secrets detection Yes - Semgrep Secrets (Pro) No

What Is Pylint?

Pylint is the oldest and most comprehensive Python code quality linter. Originally released in 2003, it has been the standard for Python static analysis longer than most of its alternatives have existed. Maintained by the PyCQA (Python Code Quality Authority) organization, Pylint performs deep semantic analysis of Python code and produces findings across five categories of messages:

  • Errors (E): Definite bugs - undefined variables, accessing attributes that don't exist on the inferred type, too many or too few arguments to a function call, invalid syntax
  • Warnings (W): Probable issues - unused variables, reimporting modules, using deprecated methods, broad exception handling, redefining variables from outer scope
  • Conventions (C): Style violations - PEP 8 naming, missing docstrings, lines too long, wrong whitespace
  • Refactoring suggestions (R): Code smell indicators - too many branches, too many local variables, duplicate code patterns, functions that are too long, classes that violate principles
  • Information (I): Informational messages about suppressed findings

Pylint's analysis goes deeper than simple pattern matching. It builds a full abstract syntax tree of Python source code, performs type inference where possible, tracks variable usage across the file and across module imports, and understands Python's object model well enough to flag incorrect attribute access on inferred types. When you tell Pylint that a variable should be a list and you call .sort() on it, Pylint can verify that .sort() is a valid list method. When a variable is potentially uninitialized in a code path, Pylint catches it.

The breadth of Pylint's checks is substantial. Its built-in message catalog includes over 400 distinct message codes. A few categories worth highlighting for Python teams:

# Pylint run - typical output on a messy Python file
src/services/user_service.py:15:4: W0612 (unused-variable)
  Unused variable 'temp_result'
src/services/user_service.py:28:0: C0301 (line-too-long)
  Line too long (127/100 characters)
src/services/user_service.py:45:8: W0703 (broad-except)
  Catching too general exception Exception
src/services/user_service.py:67:4: R0912 (too-many-branches)
  Too many branches (15/12)
src/models/product.py:12:0: E1101 (no-member)
  Instance of 'QuerySet' has no 'filter_by' member (use 'filter' for Django ORM)
Enter fullscreen mode Exit fullscreen mode

Each finding includes the file path, line number, message code, and description. Pylint also produces an overall code quality score (out of 10.0) that teams use to track quality trends over time and enforce minimum thresholds in CI/CD pipelines.

Pylint's biggest practical challenge is speed. On large Python codebases, Pylint can be slow - a 100,000-line project may take 30-60 seconds or longer. This has driven many teams to adopt Ruff (written in Rust) for the linting checks Ruff already covers, while keeping Pylint only for the deeper semantic analysis that Ruff has not yet replicated. See our Sourcery vs Pylint comparison for more context on how AI-assisted tools compare to traditional Pylint workflows.

What Is Semgrep?

Semgrep is a multi-language, programmable static analysis engine built for application security. Originally developed at Facebook and now maintained by Semgrep, Inc., it provides a rule engine that lets security teams write vulnerability detection rules using syntax that mirrors the source code being scanned - making rules far more readable and maintainable than traditional regex or AST-based approaches.

Semgrep operates across three tiers:

Community Edition (free, open source): The core Semgrep engine under LGPL-2.1. Includes single-file and single-function analysis, access to 2,800+ community rules, CI/CD integration, and no login required. This is what most developers first encounter.

Team tier ($35/contributor/month, free for up to 10 contributors): The full Semgrep AppSec Platform. Adds cross-file dataflow analysis via the Pro engine, taint tracking (tracing user input from sources to dangerous sinks across multiple files), Semgrep Assistant (AI-powered triage with 20-40% noise reduction), 20,000+ Pro rules, Semgrep Supply Chain (SCA with reachability analysis), Semgrep Secrets (credential detection with live validation), and a centralized finding management dashboard.

Enterprise (custom pricing): SSO/SAML, custom deployment, advanced compliance reporting, dedicated support, and SLA guarantees.

Semgrep's core value proposition for Python teams is its ability to detect real-world injection vulnerabilities that require understanding data flow across multiple files. A simple Semgrep taint-tracking rule looks like this:

rules:
  - id: django-sqli-taint
    mode: taint
    pattern-sources:
      - patterns:
          - pattern: request.GET.get(...)
          - pattern: request.POST.get(...)
    pattern-sinks:
      - patterns:
          - pattern: cursor.execute($QUERY)
    message: >
      User input from Django request may reach a raw SQL cursor.execute() call.
      Use parameterized queries or Django ORM instead.
    severity: ERROR
    languages: [python]
Enter fullscreen mode Exit fullscreen mode

This rule traces any value returned by request.GET.get() or request.POST.get() through any number of intermediate function calls, variable assignments, and module boundaries until it determines whether that value ever reaches cursor.execute(). If it does, Semgrep flags the vulnerability with high confidence, because it has verified the complete data flow rather than pattern-matching on the sink alone.

For Python specifically, Semgrep provides substantial coverage through rulesets like p/python (general Python security), p/django (Django-specific patterns), p/flask (Flask-specific patterns), and p/owasp-top-ten (OWASP Top 10 for Python). These rulesets are maintained by Semgrep's security research team and updated as new vulnerability patterns emerge.

For a full breakdown of Semgrep's capabilities, see our Semgrep review, Semgrep pricing guide, and Semgrep vs SonarQube comparison.

Feature-by-Feature Breakdown

Security Vulnerability Detection

This is the dimension where Semgrep and Pylint are most decisively different, and where treating them as alternatives is most mistaken.

Pylint's security coverage is incidental, not designed. Pylint catches a handful of patterns that happen to have security implications: use of eval() (W0123), use of exec() (W0122), broad except clauses that can mask security errors, and flagging access to undefined names that could indicate logic errors. These are not security rules in the SAST sense - they are code quality rules that overlap with some security concerns. Pylint has no concept of taint analysis, no awareness of what data is user-controlled vs. internally generated, no SQL injection detection, no path traversal rules, no XSS detection, no checks for weak cryptographic algorithms, and no secrets detection.

Semgrep's security coverage is the primary product. Its community registry contains 2,800+ security rules covering OWASP Top 10 categories, language-specific vulnerability patterns, and framework-specific checks. The Pro engine extends this with cross-file taint analysis - the mechanism required to detect real-world injection vulnerabilities in well-structured Python applications.

Consider a typical FastAPI application with a vulnerability buried in the data access layer:

# api/routes/products.py
from fastapi import APIRouter, Query
from services.product_service import search_products

router = APIRouter()

@router.get("/products/search")
async def search(q: str = Query(default="")):
    return await search_products(q)  # user input flows into service

# services/product_service.py
from db.product_repo import ProductRepository

async def search_products(search_term: str):
    repo = ProductRepository()
    return await repo.find_by_name(search_term)  # passed to repository

# db/product_repo.py

class ProductRepository:
    async def find_by_name(self, name: str):
        async with self.pool.acquire() as conn:
            # SQL injection - user input directly in query string
            return await conn.fetch(
                f"SELECT * FROM products WHERE name ILIKE '%{name}%'"
            )
Enter fullscreen mode Exit fullscreen mode

Pylint analyzing these three files would flag no security issues. It might check naming conventions, suggest adding type hints, or flag the f-string if it detects possible style violations - but it has no mechanism to trace q from the FastAPI route parameter through the service layer to the conn.fetch() call and identify the SQL injection. Semgrep's taint-tracking mode traces this path across all three files and flags the vulnerability with HIGH severity.

Code Quality and Style Enforcement

This is the dimension where Pylint is the authoritative tool and Semgrep offers minimal coverage.

Pylint's code quality analysis is comprehensive. Its 400+ message categories cover the full spectrum of Python code quality concerns:

  • Complexity: Cyclomatic complexity (R0912), number of local variables (R0914), number of arguments (R0913), number of branches (R0912), lines per function (R0915), and Halstead complexity metrics
  • Naming: Class names, function names, variable names, constant names, module names all checked against configurable regex patterns enforcing Python naming conventions
  • Documentation: Missing module docstrings (C0114), missing class docstrings (C0115), missing function docstrings (C0116)
  • Code hygiene: Unused imports (W0611), unused variables (W0612), reimported modules (W0404), shadowed built-ins (W0622), unnecessary pass statements (W0107)
  • Type-level issues: Attribute access on types that do not have the attribute (E1101), wrong number of arguments (E1121), using undefined names (E0602)
  • Refactoring: Duplicate code (R0801), unnecessary use of else after return (R1705), using list comprehension where a generator would suffice (R1729)

Semgrep's code quality coverage is minimal by default. The community registry includes some anti-pattern rules (avoid using assert in production code, prefer is over == for None comparisons, avoid mutable default arguments), but there are no built-in Semgrep rules for cyclomatic complexity, PEP 8 style enforcement, naming convention checks, or documentation completeness. Teams can write custom Semgrep rules to enforce code quality standards specific to their organization, but this requires investment in rule authoring that Pylint provides out of the box.

Custom Rule Authoring

Both tools support custom rules, but the development experience and target audience differ considerably.

Pylint custom checkers require Python plugin code. Writing a custom Pylint checker means implementing a Python class that inherits from BaseChecker, defining message codes and descriptions, registering AST node visitors, and packaging the checker as an installable plugin. The API is well-documented but requires understanding Pylint's internal architecture, Python AST node types, and Pylint's checker registration system. This is accessible to experienced Python developers but has a steeper learning curve than Semgrep's approach.

# Example custom Pylint checker
from pylint.checkers import BaseChecker
from pylint.interfaces import IAstroidChecker

class ForbidDirectDBAccessChecker(BaseChecker):
    __implements__ = IAstroidChecker
    name = 'forbidden-direct-db-access'
    msgs = {
        'W9001': (
            'Direct database access outside repository pattern',
            'direct-db-access',
            'Use the repository pattern for database access.'
        )
    }

    def visit_call(self, node):
        if hasattr(node.func, 'attrname'):
            if node.func.attrname in ('execute', 'raw'):
                self.add_message('direct-db-access', node=node)
Enter fullscreen mode Exit fullscreen mode

Semgrep custom rules are YAML that reads like code. The Semgrep rule format is accessible to any developer who can read Python. The same enforcement of a repository pattern looks like:

rules:
  - id: direct-db-access
    patterns:
      - pattern: $OBJ.execute(...)
      - pattern-not: $OBJ.execute($QUERY, $PARAMS)
      - pattern-not-inside: |
          class ...Repository...:
              ...
    message: >
      Direct database access detected outside a Repository class.
      Use the repository pattern for database access.
    severity: WARNING
    languages: [python]
Enter fullscreen mode Exit fullscreen mode

Any developer on the team can read and modify this rule. The Semgrep playground at semgrep.dev provides interactive rule testing - developers can paste sample code, write a rule, and immediately see whether it matches before deploying it to CI/CD. This lowers the barrier to encoding organization-specific security and quality standards dramatically compared to Pylint's plugin model.

Cross-File and Cross-Module Analysis

Both tools perform some form of cross-file analysis, but for entirely different purposes.

Pylint's cross-module analysis focuses on type and attribute correctness. When you import a class from another module and use it, Pylint can infer the class's attributes and methods from its source and flag incorrect usage. This helps catch bugs like calling a Django QuerySet method that does not exist (filter_by instead of filter), accessing an attribute that is defined only in a subclass, or passing the wrong number of arguments to a function defined in an imported module. This is genuinely valuable for catching bugs before runtime.

Semgrep's cross-file analysis (Pro engine) focuses on security data flow. The Pro engine traces the path of potentially dangerous data from user-controlled sources (request parameters, file uploads, environment variables) to dangerous operations (SQL queries, subprocess calls, deserialization). It follows data across imports, function calls, return values, and module boundaries specifically to confirm whether user input reaches a dangerous sink. This is a different analysis goal from Pylint's attribute and type checking, and it is the mechanism responsible for finding real-world injection vulnerabilities in structured applications.

Performance and CI/CD Speed

For teams configuring CI/CD pipelines, the runtime performance of both tools matters.

Pylint is slow on large codebases. A medium Python project (30,000-50,000 lines) typically takes 15-45 seconds for a full Pylint run. A large project (100,000+ lines) can take 1-3 minutes. This makes Pylint impractical for pre-commit hooks on large files but acceptable for CI/CD pipeline stages that run on push or PR. Enabling parallel processing with --jobs=4 reduces runtime but adds overhead. Many teams address this by running Pylint only on changed files in CI rather than the full codebase on every run.

Semgrep's Community Edition is faster in CI contexts. On the same codebase, Semgrep's community rules typically run in 10-30 seconds, with diff-aware scanning (checking only changed files) completing in under 10 seconds for typical PR changes. The Pro engine with cross-file analysis takes longer - typically 30 seconds to 2 minutes depending on codebase size and rule complexity - but this analysis depth is not available in Pylint at all.

For teams prioritizing pre-commit hook speed, Ruff (the Rust-based Python linter) is dramatically faster than Pylint - completing in under 1 second on most codebases - and covers a large subset of Pylint's checks. Ruff plus Semgrep is an increasingly common pairing that optimizes speed for quality checks while keeping security scanning thorough.

Pricing Comparison

Pylint Pricing

Pylint is completely free. It is an open-source community project maintained by PyCQA under the GPL-2.0 license. There is no paid tier, no premium edition, no usage limits, and no account required. Install it with pip install pylint, configure it with a .pylintrc file, and run it indefinitely on any codebase of any size. Pylint's cost is zero, now and permanently.

The practical costs are developer time: configuring Pylint to match your team's standards, managing the .pylintrc to suppress false positives or adjust thresholds, and handling the maintenance of inline # pylint: disable comments for legitimate exceptions.

Semgrep Pricing

Tier Price What You Get
Community Edition (OSS) Free Open-source engine, 2,800+ community rules, single-file analysis, CLI and CI/CD integration
Team $35/contributor/month (free for first 10 contributors) Cross-file taint analysis, 20,000+ Pro rules, Semgrep Assistant (AI triage), Semgrep Supply Chain (reachability SCA), Semgrep Secrets, centralized dashboard
Enterprise Custom Everything in Team plus SSO/SAML, custom deployment, compliance reporting, dedicated support

Cost at Different Team Sizes

Team Size Pylint Semgrep Community Semgrep Team
1-5 developers $0 $0 $0 (free for 10 contributors)
10 developers $0 $0 $0 (free for 10 contributors)
20 developers $0 $0 $8,400/year
50 developers $0 $0 $21,000/year
100 developers $0 $0 $42,000/year

For teams up to 10 contributors, the full Semgrep platform costs the same as Pylint: zero. This means small Python teams can have both comprehensive code quality linting (Pylint) and comprehensive security SAST (full Semgrep platform) at no cost. The "choose between them" framing is a false dilemma for teams under 10 contributors.

For larger teams needing Semgrep's Pro features, the $35/contributor/month covers capabilities (cross-file taint analysis, AI triage, reachability SCA) that have no Pylint equivalent - they are different categories of analysis.

Alternatives to Consider

If you are evaluating Python static analysis tools more broadly, several alternatives and combinations are worth considering alongside Semgrep and Pylint.

Ruff is the fastest Python linter available, written in Rust and implementing 800+ rules that cover a large subset of Pylint, Flake8, isort, and other Python linters. Ruff runs 10-100x faster than Pylint and has become the default choice for style enforcement and basic linting in many Python projects. For teams where Pylint's speed is a bottleneck, Ruff handles most linting concerns while Pylint retains its role for deeper semantic analysis. See our Sourcery vs Ruff comparison for context on how these tools compare in Python code quality.

Bandit is a Python-only security scanner from PyCQA - the same organization that maintains Pylint. Bandit focuses exclusively on security checks using Python AST analysis, covering approximately 80 categories of common Python security issues. It is faster and simpler than Semgrep but lacks cross-file analysis. For Python-only projects that want zero-configuration security scanning alongside Pylint's code quality checks, Bandit is a natural pairing. See our Semgrep vs Bandit comparison for a detailed breakdown of where Bandit fits relative to Semgrep.

SonarQube combines code quality and security analysis in one platform. Its Python analyzer covers security rules (OWASP Top 10 categories), code smells, bugs, duplications, and complexity in a single tool with a centralized dashboard and historical trend tracking. For teams that want to consolidate quality and security metrics in one place rather than running separate Pylint and Semgrep pipelines, SonarQube provides meaningful coverage of both dimensions. See our Semgrep vs SonarQube comparison for a direct comparison.

DeepSource provides Python-specific analysis covering both quality and security with automated fix suggestions. Its Python analyzer includes OWASP security rules, anti-pattern detection, performance checks, and code coverage analysis. It supports automated pull request fixes for many findings, reducing the manual remediation burden.

CodeAnt AI is a broader platform combining AI code review, SAST security scanning, secrets detection, and code quality analysis at $24-40/user/month. For teams that want a single vendor covering code review, security scanning, and quality analysis rather than running and maintaining separate Semgrep and Pylint configurations, CodeAnt AI provides consolidation value. It is not as configurable as Semgrep for custom security rules, but reduces tool sprawl for teams that find multi-tool pipelines difficult to manage.

Codacy integrates with Pylint (among many other linters) and adds a layer of aggregation, dashboard visualization, and PR blocking on top of existing tools. Rather than replacing Pylint, Codacy can run Pylint as part of a broader analysis pipeline that includes security tools, coverage tracking, and quality trend reporting. See our Codacy vs Semgrep comparison for how these platforms relate.

For a broader view of the Python analysis landscape, see our best code review tools for Python guide and best SAST tools comparison.

Use Cases - When to Choose Each

When Pylint Is the Right Choice

Code quality enforcement in Python projects. Any Python project that cares about maintainable code - readable names, appropriate complexity, documented functions, consistent style, early bug detection - should run Pylint. It is the most thorough Python code quality analyzer available and has been the community standard for two decades. Configuring Pylint to match your team's standards and enforcing those standards in CI/CD is one of the highest-leverage investments a Python team can make in long-term code maintainability.

Teams that need complexity and documentation metrics. Pylint's cyclomatic complexity analysis, documentation completeness checks, and code smell detection provide metrics that Semgrep does not offer. If your code review standards include "functions should not exceed complexity 10" or "all public methods must have docstrings," Pylint enforces these standards automatically.

CI/CD quality gates. Pylint's quality score (0.0-10.0) provides a quantitative metric that teams use to enforce minimum quality standards - for example, blocking merges if the quality score falls below 8.5, or failing CI if Pylint reports any Error-level findings. This creates a measurable quality floor that prevents gradual quality degradation over time.

Catching bugs before runtime. Pylint's type inference and semantic analysis catches bugs that only appear at runtime without static analysis: calling methods that do not exist on inferred types, using undefined variables, wrong number of arguments, comparing incompatible types. For Python codebases without comprehensive type annotations or test coverage, Pylint's semantic analysis catches a surprising number of real bugs.

When Semgrep Is the Right Choice

Python web applications handling user input. Any web application built on Django, Flask, or FastAPI that processes user-provided data needs security SAST. Pylint cannot detect whether user input reaches SQL queries, file system operations, subprocess calls, or deserialization functions. Semgrep's taint-tracking engine detects these paths across multi-file architectures. For web applications, Semgrep is not optional - it is the tool that catches the vulnerabilities that matter most in production.

Organizations with compliance or audit requirements. Security compliance frameworks (SOC 2, PCI-DSS, HIPAA, ISO 27001) typically require evidence of security testing in the development pipeline. Semgrep's finding reports, CI/CD integration, and rule provenance provide audit-friendly evidence that code is scanned for specific vulnerability categories. Pylint's quality reports do not satisfy security audit requirements.

Teams that need to encode custom security policies. If your organization has internal security standards - mandatory use of internal authentication wrappers, required input validation for specific API patterns, prohibited use of certain third-party libraries with known issues - Semgrep's YAML rule authoring lets you encode these as automated checks. Semgrep rules can capture organizational security knowledge and enforce it consistently across all repositories and all developers.

Multi-language stacks. If your Python backend is part of a system that also includes JavaScript frontends, Go services, Terraform infrastructure, or Kubernetes configurations, Semgrep scans all of them with the same tool, rule format, and centralized findings dashboard. Running Pylint (Python-only) alongside separate tools for each other language adds operational complexity.

Security teams managing multiple repositories. Semgrep's organization-level policy management lets security teams define rulesets that apply across all repositories, track findings centrally, and enforce security policies without requiring each development team to configure their own scanning. This is a platform-level capability that Pylint, as a per-project linter, cannot provide.

The Recommended Combined Setup

For most Python teams, the right setup is not one tool but a combination optimized for different feedback loops:

Pre-commit stage (developer workstation):

  • Ruff for fast code formatting and basic linting (sub-second execution)
  • Pylint for deeper semantic analysis and complexity checks on changed files
  • Optional: Semgrep Community Edition for quick security pattern checks

CI/CD stage (every PR):

  • Pylint full run with quality score enforcement (blocks merge below threshold)
  • Semgrep with p/python + p/django or p/flask (blocks merge on HIGH severity findings)
  • On Semgrep Team tier: full cross-file taint analysis with AI triage

This layered approach matches tool capabilities to feedback loop requirements. Fast, cheap checks run early and often. Thorough, comprehensive checks run at PR time where slower execution is acceptable. The total tooling cost for teams under 10 contributors is zero - both Pylint and the full Semgrep Team platform are free at that scale.

The Verdict

Semgrep and Pylint are not competing for the same role in your Python toolchain. They measure different properties of your code using different techniques for different purposes.

Pylint answers the question: "Is this code well-written?" It checks whether the code is clean, consistent, appropriately complex, documented, and free of obvious bugs. Every Python project should answer this question - Pylint (or Ruff for speed-sensitive contexts) is the standard tool for answering it.

Semgrep answers the question: "Is this code secure?" It checks whether user input can reach dangerous operations, whether the application uses cryptographic algorithms safely, whether secrets are hardcoded, and whether framework-specific security patterns are followed. Every Python web application should answer this question - Semgrep is the most capable open-source tool for answering it.

For solo developers and very small Python teams (1-5 contributors): Add Pylint to your CI/CD for code quality enforcement, and add the free Semgrep Community Edition for security scanning. If your project handles user input from a web interface, enable the free Semgrep Team tier for cross-file taint analysis. Cost: zero for both tools.

For small to medium Python teams (5-20 contributors): Configure Pylint with team-specific standards and enforce quality gates in CI/CD. Run Semgrep with framework-specific rulesets (p/django, p/flask) in CI/CD. If you have up to 10 contributors, the full Semgrep Team platform is free. Beyond 10 contributors, Semgrep Team at $35/contributor/month is justified for any web application handling user input - the cross-file taint analysis finds vulnerabilities that single-file scanning structurally cannot detect.

For larger teams and organizations: Consider whether consolidation tools like SonarQube (quality and security in one dashboard) or CodeAnt AI ($24-40/user/month, bundling AI review, SAST, and quality) reduce operational overhead compared to maintaining separate Pylint and Semgrep pipelines. The separate-tools approach provides the deepest coverage in each domain; the consolidated approach trades some depth for operational simplicity.

The bottom line: add Pylint to enforce code quality standards. Add Semgrep to enforce security standards. For Python teams under 10 contributors, both are free. For larger teams, the combined cost delivers coverage in two dimensions that no single tool provides at comparable depth. The right question is not "Semgrep or Pylint?" - it is "how do we configure both tools to give developers fast, accurate, actionable feedback?"

For further reading, see our how to setup Semgrep guide, our Semgrep alternatives comparison, and our best code quality tools overview.

Further Reading

Frequently Asked Questions

Is Pylint a security tool or a code quality tool?

Pylint is primarily a code quality tool, not a dedicated security scanner. It checks Python code for errors, style violations, bad practices, dead code, code complexity, and PEP 8 compliance. While Pylint does flag some patterns that have security implications - such as using broad exception handling, using eval(), or accessing undefined variables - it has no concept of taint tracking, no awareness of data flow between files, and no dedicated security rule categories comparable to what Semgrep provides. For security scanning, Pylint should be supplemented with a dedicated tool like Semgrep or Bandit. For code quality linting, Pylint remains one of the most thorough options for Python, though Ruff has become a faster and increasingly popular alternative for many Pylint checks.

Can Semgrep replace Pylint for Python code quality checks?

Semgrep can perform some code quality checks using community rules, but it is not designed to replace Pylint for general code quality linting. Pylint's code quality coverage is deeper for Python-specific concerns: it tracks variable usage, detects shadowed names, measures cyclomatic complexity, identifies unreachable code, flags unused imports, checks for missing docstrings, enforces naming conventions, and reports dozens of code smell categories. Semgrep's strengths are security analysis, custom pattern matching, and cross-language scanning. For comprehensive Python code quality, Pylint (or the faster Ruff as a Pylint alternative) covers ground that Semgrep's security-focused rule engine does not. The right answer for most Python teams is both tools: Semgrep for security, Pylint or Ruff for code quality.

Does Pylint detect security vulnerabilities in Python?

Pylint detects a limited set of patterns with security relevance, but it is not a security scanner. Its built-in checks catch: use of eval() (W0123), use of exec() (W0122), broad exception clauses that might hide security errors (W0703), and some patterns around attribute access. However, Pylint has no SQL injection detection, no cross-file taint analysis, no checks for weak cryptographic algorithms, no hardcoded password detection, no subprocess shell injection rules, and no integration with security vulnerability databases. For Python security scanning, you need a dedicated SAST tool like Semgrep (which includes 2,800+ community security rules and Pro cross-file analysis) or Bandit (which focuses exclusively on Python security patterns). Pylint and a dedicated security scanner should be run side by side, not as alternatives.

What is the difference between Semgrep and Pylint?

Semgrep and Pylint serve fundamentally different purposes despite both analyzing Python code statically. Pylint is a code quality linter: it checks for bugs, enforces coding standards, measures complexity, detects unused variables, flags style violations, and reports on code maintainability. It gives your code a quality score and helps teams write cleaner, more consistent Python. Semgrep is a security-focused SAST (Static Application Security Testing) engine: it detects vulnerabilities, traces data flow from sources to sinks, finds framework-specific security issues, and lets teams write custom security rules in YAML. The tools occupy different layers of a Python development toolchain and should be considered complementary rather than competing. In practice, many Python teams run both: Pylint or Ruff in pre-commit hooks for instant code quality feedback, and Semgrep in CI/CD pipelines for security analysis.

Is Pylint still relevant in 2026, or has Ruff replaced it?

Pylint remains relevant in 2026, but its role has narrowed as Ruff has absorbed many of its most commonly used checks at dramatically higher speed. Ruff implements over 800 rules covering Pylint's error categories, convention checks, and many refactoring suggestions, running 10-100x faster because it is written in Rust rather than Python. For teams that primarily used Pylint for style enforcement, unused import detection, and basic error checking, Ruff is now the preferred choice. However, Pylint retains advantages in areas Ruff has not yet fully replicated: deep type inference across modules, sophisticated cyclomatic complexity analysis, plugin extensibility, and some categories of semantic analysis that go beyond what Ruff currently supports. For teams that need Pylint's deeper semantic analysis, it remains the right tool. Many teams now run Ruff for fast formatting and basic linting plus Pylint for its deeper checks.

How do you run Semgrep and Pylint together in CI/CD?

Running both tools together in a CI/CD pipeline is straightforward. In GitHub Actions, you can add two separate steps: one for Pylint (pip install pylint && pylint src/) and one for Semgrep (using the semgrep/semgrep-action with a Python ruleset like p/python or p/django). Both tools exit with non-zero codes on findings, so either can be configured to block PR merges. A common pattern is to run Pylint on every commit for code quality (failing on errors and warnings), while running Semgrep in CI for security (failing only on HIGH severity findings). This keeps the feedback loops separate: Pylint enforces the team's code quality standards, while Semgrep enforces security standards. For pre-commit hooks, Pylint is typically faster on individual changed files than Semgrep, so Pylint is more commonly placed in the pre-commit stage.

What are Pylint's biggest limitations compared to Semgrep?

Pylint's biggest limitations relative to Semgrep are in security analysis, customization, and language scope. On security: Pylint has no taint tracking, no cross-file dataflow analysis, no awareness of whether user input reaches dangerous sinks, and no integration with CVE databases or security rule libraries. On customization: writing custom Pylint checkers requires implementing Python checker classes and registering them as plugins - accessible but not as simple as Semgrep's YAML rules. On language scope: Pylint is Python-only, while Semgrep supports 30+ languages with a unified rule format. Pylint is also significantly slower than alternatives like Ruff on large codebases. For code quality specifically, Pylint's limitations are less pronounced: its deep analysis of Python semantics, type inference, and code complexity is genuinely comprehensive. The tool is well-suited for its intended purpose of code quality checking; its limitations appear when teams try to use it as a security tool, which it was never designed to be.

Does Semgrep check code quality or only security?

Semgrep can check both security and code quality, but its default rulesets and community focus are heavily security-oriented. The Semgrep community registry includes some code quality rules - for example, rules flagging Python anti-patterns, deprecated API usage, or dangerous patterns in specific frameworks - but the depth and breadth of coverage for pure code quality concerns like complexity, naming conventions, docstring completeness, and style enforcement is far less than what Pylint provides. Teams can write custom Semgrep rules to enforce code quality standards specific to their codebase, and some organizations use Semgrep exclusively for this purpose. But for out-of-the-box code quality coverage comparable to Pylint's 400+ built-in message categories, Semgrep requires significant custom rule investment. Most teams use Semgrep for security and a dedicated linter (Pylint, Ruff, Flake8) for code quality.

How much does Semgrep cost compared to Pylint?

Pylint is completely free and open source under the GPL-2.0 license, with no paid tier, no account required, and no usage limits. Semgrep's Community Edition is also free - it includes the open-source CLI and 2,800+ community security rules with no account required. The full Semgrep platform (Team tier) costs $35/contributor/month but is free for teams of up to 10 contributors. This free tier includes cross-file taint analysis, AI-powered triage (Semgrep Assistant), 20,000+ Pro security rules, Semgrep Supply Chain (SCA with reachability), and Semgrep Secrets. Enterprise pricing is custom. For small Python teams, both tools are free. For larger teams that need Semgrep's Pro features beyond 10 contributors, the incremental cost is $35/contributor/month. Running both tools together on a team of 10 or fewer costs zero dollars.

Can I use Pylint plugins to add security checks?

Yes, Pylint has a plugin ecosystem that adds checks beyond its built-in set. Security-relevant plugins include pylint-django (adds Django-specific checks including some security patterns), pylint-flask (Flask-specific patterns), and various community plugins that add checks for specific frameworks or vulnerability categories. However, these plugins do not provide the depth of security analysis that dedicated SAST tools like Semgrep offer. There is no Pylint plugin that adds cross-file taint tracking, automated CVE correlation, or the equivalent of Semgrep's 20,000+ Pro security rules. Pylint plugins are better understood as extensions of its code quality analysis into specific frameworks than as security enhancements. For security scanning, a dedicated tool is necessary even with plugins.

Which tool is better for a Django project, Semgrep or Pylint?

For a Django project, both tools serve distinct roles and the most effective setup uses both. Pylint with the pylint-django plugin provides excellent code quality analysis for Django projects: it understands Django's metaclass patterns, flags improper model field usage, checks for Django-specific anti-patterns, and enforces code quality standards throughout the Django codebase. Semgrep with the p/django ruleset provides Django-specific security scanning: it detects missing CSRF protection, insecure use of mark_safe(), raw SQL via extra() and RawSQL(), overly permissive ALLOWED_HOSTS, and cross-file taint flows from Django request objects to dangerous sinks. Neither tool alone provides comprehensive coverage of both dimensions. The practical recommendation is: install pylint with pylint-django for code quality, and run Semgrep with p/django for security scanning. See our guide on Semgrep setup for detailed configuration instructions.

What alternatives exist if I want both code quality and security in one tool?

Several tools combine code quality and security analysis in a single platform, which can simplify toolchain management. SonarQube and SonarCloud provide both security rules (OWASP Top 10, injection detection) and code quality metrics (complexity, duplication, bugs, code smells) in one platform with a unified dashboard. Codacy similarly bundles security and quality checks and integrates with popular Python linters. DeepSource provides Python-specific analysis covering both security (SQL injection, XSS patterns) and quality (complexity, anti-patterns, performance issues) in one tool with automated fix suggestions. CodeAnt AI combines AI code review, SAST security scanning, and code quality analysis at $24-40/user/month - useful for teams that want consolidation around a single vendor rather than running separate tools. For teams that are comfortable managing separate tools, the Pylint-plus-Semgrep combination provides deeper coverage in each domain than any single-tool alternative at comparable cost.


Originally published at aicodereview.cc

Top comments (0)