<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by hadi tayanloo on Medium]]></title>
        <description><![CDATA[Stories by hadi tayanloo on Medium]]></description>
        <link>https://medium.com/@htayanloo?source=rss-aac7d14ebcf4------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*UgwC6oWKnvK_QPmo</url>
            <title>Stories by hadi tayanloo on Medium</title>
            <link>https://medium.com/@htayanloo?source=rss-aac7d14ebcf4------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 24 May 2026 02:29:00 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@htayanloo/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[How We Centralized Our GitLab CI Pipelines at Tapin — Part 2: Customizing and Scaling Shared…]]></title>
            <link>https://medium.com/@htayanloo/how-we-centralized-our-gitlab-ci-pipelines-at-tapin-part-2-customizing-and-scaling-shared-f2f3c1bcef12?source=rss-aac7d14ebcf4------2</link>
            <guid isPermaLink="false">https://medium.com/p/f2f3c1bcef12</guid>
            <category><![CDATA[gitlab-ci]]></category>
            <category><![CDATA[cicd]]></category>
            <category><![CDATA[gitlab]]></category>
            <category><![CDATA[cicd-pipeline]]></category>
            <category><![CDATA[automation]]></category>
            <dc:creator><![CDATA[hadi tayanloo]]></dc:creator>
            <pubDate>Fri, 07 Nov 2025 08:27:58 GMT</pubDate>
            <atom:updated>2025-11-07T08:27:58.722Z</atom:updated>
            <content:encoded><![CDATA[<h3>giHow We Centralized Our GitLab CI Pipelines at Tapin — Part 2: Customizing and Scaling Shared Templates</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lBjmbHZGUTHBsd9rht8l1Q.png" /></figure><p><em>In </em><a href="#"><em>Part 1</em></a><em> of this series, we built a shared GitLab CI repository and included it in multiple projects.</em></p><p><em>Now comes the real challenge — customization.</em></p><p>Every developer eventually says:</p><blockquote><em>“Hey, I need to skip the deploy stage just for this repo.”</em></blockquote><blockquote><em>“Can we change the Docker image for the frontend build?”</em></blockquote><p>If your shared CI templates are too rigid, teams will either fork them or hack local workarounds.</p><p>Neither is fun to maintain.</p><p>In this post, I’ll show you how we made our <strong>centralized CI repo flexible</strong>, so each project can tweak what it needs <em>without breaking the core logic</em>.</p><h3>Recap: Where We Left Off</h3><p>We had a shared repository called ci-templates, included like this:</p><pre>include:<br>  - project: &#39;tapin/ci-templates&#39;<br>    ref: &#39;v1.0.0&#39;<br>    file: &#39;/.gitlab-ci.yml&#39;</pre><p>This gave us consistent pipelines across all services.</p><p>But some projects needed small adjustments — different images, skipped stages, or extra scripts.</p><p>Time to make our shared templates <em>parameterized</em>.</p><h3>Step 1 — Add Customizable Variables</h3><p>Variables are the easiest way to let downstream projects configure behavior.</p><p>Here’s a snippet from templates/build.yml:</p><pre># templates/build.yml<br>variables:<br>  BUILD_IMAGE: docker:24<br>  ENABLE_DOCKER_PUSH: &quot;true&quot;<br><br>build_app:<br>  stage: build<br>  image: $BUILD_IMAGE<br>  services:<br>    - docker:dind<br>  script:<br>    - docker build -t &quot;$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA&quot; .<br>    - if [ &quot;$ENABLE_DOCKER_PUSH&quot; = &quot;true&quot; ]; then docker push &quot;$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA&quot;; fi</pre><p>Now any project can override these defaults in its .gitlab-ci.yml:</p><pre>variables:<br>  BUILD_IMAGE: node:22-alpine<br>  ENABLE_DOCKER_PUSH: &quot;false&quot;</pre><p>This simple pattern covers 80% of real-world customization needs.</p><h3>Step 2 — Use extends: for Job Overrides</h3><p>Sometimes you need deeper changes — for example, running an extra command or switching the deploy logic entirely.</p><p>That’s where extends: comes in.</p><p>In ci-templates we defined reusable job “bases”:</p><pre># templates/test.yml<br>.test_base:<br>  stage: test<br>  image: python:3.12<br>  script:<br>    - pytest -v</pre><p>In a downstream repo, we can now extend and modify it:</p><pre>unit_tests:<br>  extends: .test_base<br>  script:<br>    - echo &quot;Running with custom settings&quot;<br>    - pytest --maxfail=1 --disable-warnings -q</pre><p>This merges the upstream definition with local changes — keeping inheritance clean and maintainable.</p><h3>Step 3 — Conditional Jobs with rules:</h3><p>Some repos don’t deploy at all (e.g., internal libraries).</p><p>Others deploy to different environments.</p><p>We used rules: to make deploy jobs optional:</p><pre># templates/deploy.yml<br>deploy_production:<br>  stage: deploy<br>  image: alpine<br>  script:<br>    - echo &quot;Deploying to production...&quot;<br>  rules:<br>    - if: &#39;$ENABLE_DEPLOY == &quot;true&quot;&#39;<br>      when: on_success<br>    - when: never</pre><p>Then in each project:</p><pre>variables:<br>  ENABLE_DEPLOY: &quot;false&quot;  # skip deploy for this project</pre><p>This gave teams full control — no copy-paste, no if-else chaos.</p><h3>Step 4 — Add Template Parameters with !reference</h3><p>Another useful trick: !reference lets you reuse script sections without rewriting them.</p><p>For instance:</p><pre>.build_steps:<br>  script:<br>    - echo &quot;Linting...&quot;<br>    - docker build -t &quot;$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA&quot; .<br><br>build_and_push:<br>  stage: build<br>  image: docker:24<br>  services: [ &quot;docker:dind&quot; ]<br>  script:<br>    - !reference [.build_steps, script]<br>    - docker push &quot;$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA&quot;</pre><p>It’s like calling a function inside YAML.</p><p>This keeps your core jobs modular and easier to maintain.</p><h3>Step 5 — Include Multiple Templates Conditionally</h3><p>When projects differ significantly (e.g., Node.js vs Python), we split templates per tech stack and conditionally include them.</p><p>Example in the project .gitlab-ci.yml:</p><pre>variables:<br>  TECH_STACK: &quot;python&quot;<br><br>include:<br>  - project: &#39;tapin/ci-templates&#39;<br>    ref: &#39;v1.0.0&#39;<br>    file:<br>      - &#39;/templates/common.yml&#39;<br>      - &#39;/templates/${TECH_STACK}.yml&#39;</pre><p>GitLab dynamically expands ${TECH_STACK} and picks the right file.</p><p>That way, frontend and backend teams use the same system, but load their own specialized templates.</p><h3>Step 6 — Safe Rollouts and Versioning</h3><p>We learned early that one mistake in a shared pipeline can break everything.</p><p>So, we implemented a <strong>staged rollout strategy</strong>:</p><ol><li><strong>Sandbox project:</strong> test every change there first.</li><li><strong>Tag releases</strong> (v1.0.0, v1.1.0, etc.) in the ci-templates repo.</li><li>Projects gradually upgrade by changing ref: manually.</li></ol><p>For example:</p><pre>include:<br>  - project: &#39;tapin/ci-templates&#39;<br>    ref: &#39;v1.1.0&#39;<br>    file: &#39;/.gitlab-ci.yml&#39;</pre><p>This allowed us to move fast but never break existing pipelines.</p><h3>Step 7 — Debugging Included Pipelines</h3><p>When something breaks, the first instinct is to look at the local .gitlab-ci.yml —</p><p>but remember, your jobs come from multiple includes.</p><p>Use GitLab’s <strong>“CI Lint” → “Include configuration”</strong> feature.</p><p>Paste your local file there — it expands all includes and shows the <em>final merged YAML</em>.</p><p>It’s a life-saver when debugging variable inheritance or job duplication.</p><h3>Step 8 — Bonus: Custom Job Templates for Teams</h3><p>Once the foundation was stable, teams started adding their own shared jobs under ci-templates/team/.</p><p>Example:</p><pre>templates/<br>├── team/<br>│   ├── data.yml<br>│   ├── platform.yml<br>│   └── devops.yml</pre><p>Each file defined team-specific steps — linting conventions, deployments, or security scans.</p><p>This structure kept autonomy while preserving consistency.</p><h3>Lessons Learned</h3><p>A few takeaways after scaling shared CI across 30+ repos:</p><ul><li><strong>Start small.</strong> One template for build/test is enough. Expand later.</li><li><strong>Document every variable.</strong> Downstream users need clear guidance.</li><li><strong>Always version and test.</strong> Don’t include main unless you enjoy chaos.</li><li><strong>Encourage feedback.</strong> Your CI repo is a shared product — treat it like one.</li><li><strong>Automate validation.</strong> A bad YAML merge can take down multiple pipelines.</li></ul><h3>Final Thoughts</h3><p>Centralizing GitLab CI/CD wasn’t just a technical improvement — it was cultural.</p><p>It taught our team to think in systems, not silos.</p><p>Today, every new project at Tapin starts with a 5-line .gitlab-ci.yml,</p><p>and yet it inherits the full power of our production-grade pipeline.</p><pre>include:<br>  - project: &#39;tapin/ci-templates&#39;<br>    ref: &#39;v2.0.0&#39;<br>    file: &#39;/.gitlab-ci.yml&#39;</pre><p>That’s it. CI in seconds, with years of refinement baked in.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f2f3c1bcef12" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How We Centralized Our GitLab CI Pipelines at Tapin — Part 1: Building the Shared CI Repository]]></title>
            <link>https://medium.com/@htayanloo/how-we-centralized-our-gitlab-ci-pipelines-at-tapin-part-1-building-the-shared-ci-repository-035db546625f?source=rss-aac7d14ebcf4------2</link>
            <guid isPermaLink="false">https://medium.com/p/035db546625f</guid>
            <category><![CDATA[cicd-pipeline]]></category>
            <category><![CDATA[gitlab]]></category>
            <category><![CDATA[automation]]></category>
            <category><![CDATA[gitlab-ci]]></category>
            <dc:creator><![CDATA[hadi tayanloo]]></dc:creator>
            <pubDate>Fri, 07 Nov 2025 08:21:53 GMT</pubDate>
            <atom:updated>2025-11-07T08:21:53.516Z</atom:updated>
            <content:encoded><![CDATA[<h3>How We Centralized Our GitLab CI Pipelines at Tapin — Part 1: Building the Shared CI Repository</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lBjmbHZGUTHBsd9rht8l1Q.png" /></figure><p><em>When your organization grows, your CI pipelines tend to grow with it — and sometimes they grow in all directions.</em></p><p>At Tapin, we started with a handful of projects — an API here, a worker there — each with its own .gitlab-ci.yml.</p><p>Over time, every repository had slightly different pipelines. A typo here, a missing variable there, and maintenance quickly became a nightmare.</p><p>So, we decided to <strong>centralize our GitLab CI/CD configuration</strong> into one reusable repository — a single source of truth that every service could include and customize when needed.</p><p>This article (Part 1 of 2) explains exactly how we did it:</p><p>how to create a shared CI repo, include it in other projects, and keep it version-safe.</p><h3>Why Centralize GitLab CI?</h3><p>If you manage more than a few services, you’ve probably run into this pattern:</p><pre>api/.gitlab-ci.yml<br>frontend/.gitlab-ci.yml<br>worker/.gitlab-ci.yml</pre><p>All three might define the same jobs: build, test, deploy, etc.</p><p>When one needs a tweak — say a new Docker image tag — you must update all three.</p><p>Centralization solves that.</p><p>Instead of duplicating, you write those jobs <strong>once</strong> in a common repo and let everyone else <strong>include</strong> it.</p><h3>Step 1 — Create the Shared CI Repository</h3><p>We created a new repo called <strong>ci-templates</strong>.</p><p>Its purpose: hold every reusable CI definition.</p><p>Here’s the structure we started with:</p><pre>ci-templates/<br>├── .gitlab-ci.yml<br>├── templates/<br>│   ├── build.yml<br>│   ├── test.yml<br>│   └── deploy.yml<br>└── README.md</pre><p>The root .gitlab-ci.yml acts as an entry point that includes the sub-templates:</p><pre># ci-templates/.gitlab-ci.yml<br>include:<br>  - local: &#39;templates/build.yml&#39;<br>  - local: &#39;templates/test.yml&#39;<br>  - local: &#39;templates/deploy.yml&#39;</pre><p>Each file inside templates/ defines one stage or job.</p><p>For example, templates/build.yml:</p><pre># templates/build.yml<br>stages:<br>  - build<br><br>build_app:<br>  stage: build<br>  image: docker:24<br>  services:<br>    - docker:dind<br>  script:<br>    - docker build -t &quot;$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA&quot; .<br>    - docker push &quot;$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA&quot;</pre><p>This file is now reusable in any project, no copy-paste required.</p><h3>Step 2 — Include It in a Downstream Project</h3><p>In each project (e.g., api, frontend), we replaced the entire .gitlab-ci.yml with a single include: statement.</p><p>Example for the api repository:</p><pre># api/.gitlab-ci.yml<br>include:<br>  - project: &#39;tapin/ci-templates&#39;<br>    ref: &#39;v1.0.0&#39;        # Pin to a version tag for safety<br>    file: &#39;/.gitlab-ci.yml&#39;</pre><p>And that’s it.</p><p>Once committed, the project automatically gets all stages and jobs from the ci-templates repo.</p><p>GitLab merges the included YAMLs at runtime — as if you had written them locally.</p><h3>Step 3 — Define Shared Variables</h3><p>We added global variables inside ci-templates/.gitlab-ci.yml so every job could rely on consistent environment settings:</p><pre>variables:<br>  DOCKER_DRIVER: overlay2<br>  DOCKER_TLS_CERTDIR: &quot;&quot;<br>  IMAGE_TAG: &quot;$CI_COMMIT_SHA&quot;</pre><p>If a project needs a different value, it can simply override it in its own .gitlab-ci.yml (we’ll cover this in Part 2).</p><h3>Step 4 — Pin Versions with ref</h3><p>One major lesson we learned early:</p><p><strong>Never use ref: main for production pipelines.</strong></p><p>If someone updates ci-templates, all projects that include main instantly change — sometimes breaking builds.</p><p>Instead, always use a <strong>tag</strong> or <strong>commit SHA</strong>:</p><pre>include:<br>  - project: &#39;tapin/ci-templates&#39;<br>    ref: &#39;v1.0.0&#39;<br>    file: &#39;/.gitlab-ci.yml&#39;</pre><p>Then, when you release new CI updates, bump the tag (v1.1.0, v1.2.0, etc.) and let projects upgrade when ready.</p><p>This versioning approach gave us stability and predictability across dozens of services.</p><h3>Step 5 — Test Before You Roll Out</h3><p>Before merging new templates, we always test them in a sandbox project that uses the same include.</p><p>We even built a small <strong>CI validation job</strong> inside ci-templates:</p><pre>validate_ci:<br>  stage: test<br>  image: registry.gitlab.com/gitlab-org/ci-cd/gitlab-ci-lint<br>  script:<br>    - curl --header &quot;PRIVATE-TOKEN: $TOKEN&quot; \<br>      --form &quot;content=$(cat .gitlab-ci.yml)&quot; \<br>      https://gitlab.com/api/v4/ci/lint</pre><p>This ensures our shared templates always stay syntactically valid.</p><h3>Step 6 — Organize by Functionality</h3><p>As the repo grew, we split templates by domain:</p><pre>templates/<br>├── docker/<br>│   ├── build.yml<br>│   └── publish.yml<br>├── test/<br>│   ├── python.yml<br>│   └── go.yml<br>└── deploy/<br>    ├── staging.yml<br>    └── production.yml</pre><p>Each project could include just what it needed:</p><pre>include:<br>  - project: &#39;tapin/ci-templates&#39;<br>    ref: &#39;v1.0.0&#39;<br>    file:<br>      - &#39;/templates/docker/build.yml&#39;<br>      - &#39;/templates/test/python.yml&#39;</pre><p>This modular structure kept the pipeline lightweight and flexible.</p><h3>Results</h3><p>By the time we finished Part 1 of this journey, we had:</p><ul><li>One <strong>centralized CI repo</strong> reused by more than 20 services.</li><li>Unified Docker build standards and naming conventions.</li><li>Easier maintenance: fix once, apply everywhere.</li><li>Safer rollouts thanks to versioned includes.</li></ul><p>In Part 2, I’ll show you how we made those shared templates <strong>customizable</strong> — letting each team override variables, toggle stages, and extend jobs without touching the core.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=035db546625f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building Software with AI, Not by AI]]></title>
            <link>https://medium.com/@htayanloo/building-software-with-ai-not-by-ai-cf1b217dbd56?source=rss-aac7d14ebcf4------2</link>
            <guid isPermaLink="false">https://medium.com/p/cf1b217dbd56</guid>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[developer-tools]]></category>
            <category><![CDATA[prompt-design]]></category>
            <category><![CDATA[software-engineering]]></category>
            <dc:creator><![CDATA[hadi tayanloo]]></dc:creator>
            <pubDate>Fri, 24 Oct 2025 12:23:24 GMT</pubDate>
            <atom:updated>2025-10-24T12:58:13.103Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1OkS9LLh4MGLRpu1dzdpzg.png" /></figure><h3>A Practical Framework to Keep Cursor (and Any AI) Aligned with Your Vision</h3><p>AI-powered coding assistants like <strong>Cursor</strong>, <strong>GitHub Copilot</strong>, and <strong>Codeium</strong> are transforming how developers write software.</p><p>They generate functions, design APIs, and even refactor architectures in seconds.</p><p>But here’s the problem: <strong>AI doesn’t understand boundaries.</strong></p><p>Without a clear framework, it will happily rename your core modules, rewrite business logic, or swap your ORM for a shiny new one — because <em>technically</em>, it’s not wrong.</p><p>If you’ve ever told an AI <em>“just fix this part”</em> and ended up with a refactored project that broke five other things… you know exactly what I mean.</p><p>This article introduces a <strong>structured process</strong> to turn AI from a chaotic “code generator” into a <strong>disciplined teammate</strong> who follows your standards, respects your architecture, and delivers what you actually want.</p><h3>The Core Problem: AI Doesn’t Know Context</h3><p>AI models are amazing at local reasoning — they can see patterns in a single file, optimize loops, and infer relationships.</p><p>But they <strong>don’t have your mental map</strong> of the project. They don’t know why your architecture looks the way it does, or why one function must never be renamed.</p><p>That’s why they “improve” things you never asked for.</p><p>The solution isn’t to fight AI — it’s to <strong>give it a frame</strong>: a set of guardrails that define your project’s identity, architecture, and rules of engagement.</p><h3>The Solution: A Two-Layer Framework</h3><p>We use two layers to control AI development:</p><ol><li><strong>Project Rules (The Law)</strong> — A fixed document (PROJECT_RULES.md) that defines what the project <em>is</em>, what tools it uses, and what can/can’t be touched.</li><li><strong>Task Protocol (The Procedure)</strong> — A structured way of asking for changes so each request stays scoped, predictable, and reviewable.</li></ol><h3>Layer 1 — The Project Rules File</h3><p>Think of PROJECT_RULES.md as your <strong>constitution</strong>.</p><p>It’s what every AI and developer must obey.</p><h3>Service Mission</h3><p>Explain what this service does, who uses it, and what can never go wrong.</p><blockquote>Example: “ Wallet Service manages internal user balances. It must guarantee transaction consistency. Downtime should never result in lost or duplicated transactions.”</blockquote><h3>Sensitivity &amp; Compliance</h3><p>List what’s legally or operationally risky.</p><blockquote>Handles personal and financial data. Withdrawal requests require human approval. OTP codes must not be logged.</blockquote><h3>Responsibility &amp; Boundaries</h3><p>Define what this service owns — and what it does not.</p><blockquote>Owns wallet balance and ledger. Does NOT handle SMS, PDF invoices, or user authentication.</blockquote><h3>Architecture &amp; Tech Stack</h3><p>Lock the language, frameworks, and folder structure.</p><blockquote>Go 1.23 + chi. No ORM. Folder layout: /src/domain, /src/app, /src/infrastructure, /src/interfaces.</blockquote><h3>Code Quality &amp; Runtime Rules</h3><p>Define coding discipline: error handling, transactions, logs, tests, and performance.</p><blockquote>All DB writes must use explicit transactions. Functions must stay under 50 lines. Logs must be JSON-formatted.</blockquote><h3>Collaboration Contract with the AI</h3><p>Explain <em>how</em> the AI must deliver code.</p><blockquote>No renaming without permission. Every response must include full file content (// FILE: path) and a final summary.</blockquote><p>Once written, this file becomes the <strong>truth</strong>.</p><p>AI tools like Cursor can then be told:</p><blockquote><em>“Follow PROJECT_RULES.md. Never deviate unless explicitly allowed.”</em></blockquote><h3>Layer 2 — The Task Protocol</h3><p>Most chaos happens not in the rules, but in how you <em>ask</em>.</p><p>A vague prompt like “Add withdrawal support” invites disaster.</p><p>Instead, every request should follow this six-part structure:</p><pre>CONTEXT: <br>Where we are, and what the project does.<br><br>GOAL:<br>The purpose of this task (what we want to add/change).<br><br>SCOPE:<br>Exactly which files or folders the AI may touch.<br><br>DO NOT TOUCH:<br>Which files, modules, or layers must stay untouched.<br><br>DELIVERABLE:<br>Expected output (e.g., full code, migration file, or summary).<br><br>FORMAT:<br>How to present it (full file content, `// FILE:` headers, and summary).This keeps the AI <em>inside its box.</em></pre><p>This keeps the AI <em>inside its box.</em></p><h3>Example Task (for Cursor or Any AI IDE)</h3><pre>CONTEXT:<br>We are in the Wallet Service project. Follow PROJECT_RULES.md strictly.<br><br>GOAL:<br>Add a new &quot;Top-Up Request&quot; feature. A user can request to recharge their wallet. The system should create a pending record but not credit the balance yet.<br><br>SCOPE:<br>Create files under /src/app/topup, /src/domain/topup, /src/infrastructure/repo/topup_repo.go, and /src/interfaces/http/topup_handler.go.<br><br>DO NOT TOUCH:<br>Existing wallet logic, transaction manager, or authentication.<br><br>DELIVERABLE:<br>Show the FULL final content of each file created/modified.<br>Prefix each with `// FILE: path/to/file.go`.<br>At the end, add a SUMMARY with:<br>- request/response schema<br>- data flow<br>- TODOs or unimplemented validation.<br><br>FORMAT:<br>Code only + final SUMMARY. No commentary.</pre><p>No guesswork. No refactoring surprises. No scope creep.</p><h3>The Deep Insight</h3><blockquote><em>AI isn’t dangerous because it’s wrong.</em></blockquote><blockquote><em>It’s dangerous because it’s </em>too confident when it’s slightly wrong.</blockquote><p>Your job is to narrow its confidence range — to define <strong>where it’s allowed to be creative</strong> and where it must stay obedient.</p><p>This framework achieves that balance.</p><h3>Bonus: Automating Rule Extraction</h3><p>If you’re a product owner or tech lead who doesn’t want to manually write all these rules, use the following <strong>Mother Prompt</strong>.</p><p>This prompt will make the AI interview you through 32 deep questions — and automatically generate your PROJECT_RULES.md.</p><h3>The Mother Prompt</h3><p>Copy and paste this into Cursor, ChatGPT, or any LLM IDE, then say:</p><blockquote><em>“Run this for my [project name].”</em></blockquote><pre>You are my &quot;Project Rules Extractor &amp; Builder.&quot;<br><br>Your job has TWO phases:<br><br>PHASE 1 — ASK ME QUESTIONS  <br>PHASE 2 — GENERATE PROJECT_RULES.md<br><br>GLOBAL RULES:<br>- Do NOT skip questions or assume answers.<br>- If an answer isn’t provided, write &quot;Not specified yet&quot;.<br>- Follow the process strictly.<br><br>PHASE 1:<br>Ask me 32 questions in 6 steps about:<br>1. Service Mission  <br>2. Sensitivity &amp; Risk  <br>3. Boundaries &amp; Ownership  <br>4. Architecture &amp; Technology  <br>5. Code Quality &amp; Process  <br>6. Collaboration Contract<br><br>Ask all questions from each step in one message, wait for my answers, then continue.<br><br>PHASE 2:<br>After all 32 answers, create PROJECT_RULES.md with these sections:<br>1. SERVICE MISSION  <br>2. SENSITIVITY &amp; COMPLIANCE  <br>3. RESPONSIBILITY &amp; BOUNDARIES  <br>4. ARCHITECTURE &amp; TECH STACK (LOCKED)  <br>5. CODE QUALITY &amp; RUNTIME RULES  <br>6. COLLABORATION CONTRACT WITH THE AI<br><br>If anything was &quot;Not specified yet&quot;, include it as-is — do not guess.  <br>Stop after producing PROJECT_RULES.md. No code generation.  </pre><h3>How to Use It</h3><ol><li>Paste the Mother Prompt into your AI tool (Cursor, ChatGPT, Claude, etc.).</li><li>Say something like:</li></ol><p>“Run this for the Wallet Service project.”</p><ol><li>Answer the 32 questions step-by-step.</li><li>When done, the AI will generate your full PROJECT_RULES.md.</li><li>Save it in your repo root and tell Cursor:</li></ol><ul><li>“Follow PROJECT_RULES.md strictly for all future tasks.”</li></ul><p>That’s it — your AI will finally build <strong>your</strong> software, not its own version of it.</p><h3>Final Thoughts</h3><p>AI copilots like Cursor can be revolutionary — but only when you give them structure.</p><p>Without discipline, they’ll code faster… in the wrong direction.</p><p>With <strong>Project Rules</strong> and <strong>Task Protocol</strong>, you transform AI from a code generator into a <strong>collaborative engineer</strong> who understands context, architecture, and responsibility.</p><p>Stop building software <em>by</em> AI.</p><h3>Full Prompt for start project with all policy :</h3><pre>You are my &quot;Project Rules Extractor &amp; Builder.&quot;<br><br>Your job has TWO phases, and you MUST ALWAYS do them in order:<br><br>PHASE 1 — ASK ME QUESTIONS  <br>PHASE 2 — GENERATE PROJECT_RULES.md<br><br>IMPORTANT GLOBAL RULES:<br>- Do NOT skip any question.<br>- Do NOT invent answers.<br>- Do NOT assume. If the answer is not given yet, explicitly say &quot;Not specified yet&quot; and move on.<br>- Keep everything structured and deterministic. No marketing tone. No fluff.<br>- You MUST stay inside the process described below. Do not switch topics.<br><br>====================================================<br>PHASE 1 — ASK ME QUESTIONS<br>====================================================<br><br>You will ask me 32 questions in 6 steps.  <br>You MUST go step by step.  <br>In each step:<br>1. Ask ALL questions from that step in a single message.<br>2. Wait for my answer.<br>3. After I answer, continue to the next step.<br><br>Do NOT mix steps. Do NOT jump ahead.<br><br>The steps and questions are:<br><br>-------------------------<br>STEP 1. SERVICE MISSION / VISION<br>(Goal: understand what this service is and why it exists)<br><br>Q1. What real-world problem does this service solve? (Describe in practical terms, not buzzwords.)<br>Q2. Who is the main user/consumer of this service? (Human end-user, internal service, admin panel, mobile app, etc.)<br>Q3. Is this service exposed directly to the internet, or is it only called internally by other services?<br>Q4. If this service goes down, what breaks in the business?<br>Q5. What is the one thing this service absolutely must ALWAYS get right, even in failure conditions? (For example: &quot;wallet balance must never be wrong&quot;)<br><br>-------------------------<br>STEP 2. SENSITIVITY / RISK / NO-GO ZONES<br>(Goal: find legal, security, business red lines)<br><br>Q6. Does this service handle sensitive data? (money, identity, personal data, location, transaction history, etc.)<br>Q7. What data or actions, if leaked or modified, would create legal, financial, or trust damage?<br>Q8. Which actions MUST require human approval and must NOT be fully automated? (like withdrawal approval, deleting accounts, etc.)<br>Q9. Are there any logging/retention constraints? (For example: &quot;Do not log OTP&quot;, &quot;We cannot store live GPS longer than X&quot;, &quot;GDPR applies&quot;, etc.)<br><br>-------------------------<br>STEP 3. BOUNDARIES / RESPONSIBILITY<br>(Goal: define what belongs to this service and what does NOT)<br><br>Q10. Which responsibilities are OWNED by this service and must live here (it is the source of truth for them)?<br>Q11. Which related responsibilities are explicitly NOT owned here and should live in other services? (For example: sending SMS, generating PDF invoices, doing KYC, etc.)<br>Q12. Which other internal services or external systems does this service depend on, and for what? (e.g. Auth service for user_id, Pricing service for shipping quote...)<br>Q13. Is this service going to expose a public API that external clients/mobile apps call directly, or is it internal-only?<br>Q14. Do you expect this service to version its API (v1, v2...), or is versioning not required yet?<br><br>-------------------------<br>STEP 4. ARCHITECTURE / TECHNOLOGY / FOLDERS<br>(Goal: lock in tech stack, folder layout, style of separation)<br><br>Q15. What language and main framework MUST be used? (Example: Go 1.23 + chi; Python + Django; Node + Express)<br>Q16. Do you want a clean layered architecture (domain / application / interfaces / infrastructure), or are you ok with a simpler &quot;routes + repo&quot; style?<br>Q17. Should business logic be separated from HTTP handlers and database code? (YES/NO. If YES, describe how you imagine that separation.)<br>Q18. Which layering model do you prefer?  <br>     Option A: domain (entities, rules) / application or usecase (orchestration) / interfaces (HTTP, gRPC...) / infrastructure (DB, Redis...)  <br>     Option B: a simpler structure (e.g. api/, repository/, models/)  <br>     Option C: [Describe your own]<br>Q19. What folder structure do you want at the top level of the repo? (List folders exactly as you want them named. Example:<br>      /src/domain<br>      /src/app<br>      /src/infrastructure<br>      /src/interfaces<br>      /migrations<br>      /tests<br>   Or your own.)<br>Q20. Which frameworks / libraries are ALLOWED?<br>Q21. Which frameworks / libraries are FORBIDDEN? (Example: &quot;No ORM like gorm&quot;, &quot;No FastAPI&quot;, &quot;No mongoose&quot;, etc.)<br>Q22. Do you have naming/style rules? (Struct names, file names, package names, snake_case vs camelCase, etc.)<br><br>-------------------------<br>STEP 5. QUALITY / PROCESS / SAFETY<br>(Goal: define how strict we are in code behavior)<br><br>Q23. In a tradeoff, which is more important: fast delivery or absolute correctness/safety? (Choose one, and explain.)<br>Q24. Do you want functions to stay small and single-responsibility, or is large/monolithic ok for now?<br>Q25. How should error handling work? (Bubble errors up and log them? Swallow noncritical errors? Panic/throw?)<br>Q26. How should database transactions be handled?  <br>     - A) A transaction is created at the top of the use case and passed down explicitly  <br>     - B) Each repository method starts/commits transactions internally  <br>     - C) You don&#39;t care / Not decided<br>Q27. What is your policy on tests for now?  <br>     - A) Every feature MUST ship with tests  <br>     - B) For now, code can ship without tests, but each response must include a TODO list of recommended tests  <br>     - C) We will add tests later manually; don&#39;t even generate TODOs<br>Q28. What is your logging policy?  <br>     - Structured JSON logs with consistent fields?  <br>     - Simple print/debug logs ok for now?  <br>     - Sensitive fields must NEVER be logged?<br>Q29. Do you have performance expectations?  <br>     (Max response time? Max RPS you expect on critical paths? Any &quot;must be under 1s&quot; constraints?)<br><br>-------------------------<br>STEP 6. COLLABORATION CONTRACT WITH THE AI<br>(Goal: control how code is generated so it doesn&#39;t go rogue)<br><br>Q30. Are renames and refactors of existing code allowed WITHOUT your explicit approval? (YES/NO. If NO, say &quot;No rename/refactor unless I explicitly approve it.&quot;)<br>Q31. When the AI thinks something unrelated needs improvement, should it:<br>     - A) apply the refactor directly<br>     - B) leave the code unchanged and just write a TODO suggestion? (Pick A or B.)<br>Q32. When the AI returns code for a task, how should it present the output?<br>     - Should it show FULL FILE CONTENT for every created/modified file, prefixed with `// FILE: path/to/file.ext`?<br>     - Should it also include a final SUMMARY section with:<br>       • request/response schema<br>       • data flow / lifecycle<br>       • TODOs / risks that are NOT implemented yet<br>     Describe exactly what format you expect.<br><br>END OF PHASE 1.<br><br>After asking all 32 questions and receiving all answers, proceed to PHASE 2.<br><br><br>====================================================<br>PHASE 2 — GENERATE PROJECT_RULES.md<br>====================================================<br><br>Now you MUST generate a single document called PROJECT_RULES.md.<br><br>PROJECT_RULES.md MUST have the following sections, in this exact order and with these exact headings:<br><br>1. SERVICE MISSION<br>   - What this service does<br>   - Who uses it<br>   - Critical guarantee (the thing that must never be wrong)<br>   - Impact of downtime<br><br>2. SENSITIVITY &amp; COMPLIANCE<br>   - Sensitive data handled<br>   - Legal / trust / security red lines<br>   - Actions requiring human approval<br>   - Logging &amp; retention constraints<br><br>3. RESPONSIBILITY &amp; BOUNDARIES<br>   - What this service OWNS (source of truth)<br>   - What this service EXPLICITLY does NOT own<br>   - Upstream/downstream dependencies (who we call / who calls us)<br>   - Public vs internal API<br>   - API versioning expectations<br><br>4. ARCHITECTURE &amp; TECH STACK (LOCKED)<br>   - Approved language, runtime version, frameworks, libraries<br>   - Forbidden libraries / patterns<br>   - Required folder structure (list folders exactly)<br>   - Required layering model (e.g. domain / application / interfaces / infrastructure)<br>   - Naming conventions<br>   - Rule: &quot;Do not introduce new frameworks or libraries unless explicitly approved&quot;<br>   - Rule: &quot;Consistency with existing code is more important than theoretical best practice&quot;<br><br>5. CODE QUALITY &amp; RUNTIME RULES<br>   - Error handling policy<br>   - Transaction handling policy<br>   - Logging policy<br>   - Testing policy<br>   - Performance expectations / SLAs<br>   - Priority: speed vs correctness<br>   - Function size / readability expectations<br><br>6. COLLABORATION CONTRACT WITH THE AI<br>   - Refactor / rename policy<br>   - Allowed scope of a single task (which folders the AI may touch per request)<br>   - Output format requirement:<br>     * For each touched file, AI MUST output the FULL final content<br>     * Each file must be prefixed with `// FILE: relative/path`<br>     * AI MUST also output a final `SUMMARY` section with:<br>       - request/response schema<br>       - data flow in plain language<br>       - TODO suggestions and security concerns<br>   - Rule: &quot;If you think something outside the current task needs improvement, DO NOT change it. Add it as a TODO in SUMMARY.&quot;<br><br>IMPORTANT:<br>- If any answer from PHASE 1 was &quot;Not specified yet&quot;, you MUST still include that line in PROJECT_RULES.md rather than guessing. Explicitly write &quot;Not specified yet&quot;.<br>- The PROJECT_RULES.md MUST be clean, direct, and implementation-focused. Do NOT include marketing language.<br>- After you produce PROJECT_RULES.md, STOP. Do not generate any code.<br><br>====================================================<br><br>GENERAL BEHAVIOR REMINDERS:<br>- Stay focused. Do not invent features, endpoints, or capabilities not explicitly confirmed.<br>- You are not allowed to &quot;improve&quot; scope for me automatically. You only gather, and then you summarize into PROJECT_RULES.md.<br>- You are not allowed to generate code in PHASE 1.<br>- You are not allowed to skip PROJECT_RULES.md in PHASE 2.<br><br>BEGIN NOW.</pre><h3>How to Use the Project Rules in Cursor</h3><p>Once your PROJECT_RULES.md is ready, you can now connect it directly to your <strong>Cursor workflow</strong> — turning the AI from a generic code generator into a reliable, rule-following teammate.</p><p>Here’s how to do it step-by-step 👇</p><h3>🔹 1. Place the Rules in Your Repository</h3><p>Save the file at the root of your project (right beside go.mod, README.md, or package.json).</p><p>Cursor automatically reads files in your workspace as context, so having PROJECT_RULES.md there means it can “see” it.</p><h3>🔹 2. Instruct Cursor to Follow It</h3><p>Whenever you create or edit code with Cursor, start your request with this line:</p><pre>Follow PROJECT_RULES.md strictly before doing anything.</pre><p>Then describe your task using the structured format below.</p><p>Cursor works best when your instructions are precise and bounded.</p><h3>🔹 3. Use the Task Protocol for Every Change</h3><p>Use this exact structure in each AI prompt (either in chat or command mode):</p><pre>CONTEXT:<br>We are in the ShortLink project. Follow PROJECT_RULES.md strictly.<br><br>GOAL:<br>Add a new endpoint to show detailed analytics per short link (aggregated by OS, browser, and country).<br><br>SCOPE:<br>You may only modify:<br>- /src/app/analytics/<br>- /src/domain/analytics/<br>- /src/interfaces/http/analytics_handler.go<br>- /tests/analytics_test.go<br><br>DO NOT TOUCH:<br>- Any existing link creation or redirect logic.<br>- Database schema or migrations.<br>- Authentication or Keycloak integration.<br><br>DELIVERABLE:<br>Provide full final content for every file you create or modify.<br>Each file must begin with `// FILE: path/to/file.go`<br>and end with a SUMMARY section explaining:<br>- request/response schema<br>- data flow<br>- TODOs for things outside this scope<br><br>FORMAT:<br>Code only + final SUMMARY. No extra commentary.</pre><h3>🔹 4. Review and Commit</h3><p>Cursor will generate full files instead of small diffs.</p><p>That means you can:</p><ul><li>copy/paste directly into your codebase,</li><li>review line-by-line before committing,</li><li>and verify that nothing outside your defined scope changed.</li></ul><p>If it ever modifies something unexpected, just reply:</p><blockquote><em>“That violates PROJECT_RULES.md — revert that change.”</em></blockquote><p>The AI will automatically correct itself.</p><h3>🔹 5. Repeat for Every Feature</h3><p>Every new feature or fix you want to add should be a separate, scoped request.</p><p>This prevents chain reactions like “auto refactors” or “silent architecture changes.”</p><p>With time, you’ll build a stable rhythm:</p><ul><li>PROJECT_RULES.md defines <strong>how</strong> to work.</li><li>Each Task Prompt defines <strong>what</strong> to build.</li></ul><p>Together, they form a repeatable and predictable development process — AI code that always fits your architecture.</p><h3>💡 Pro Tip</h3><p>If your project grows, you can version your rules file (e.g., PROJECT_RULES_v2.md) and tell Cursor:</p><blockquote><em>“Follow PROJECT_RULES_v2.md instead of the previous one.”</em></blockquote><p>This keeps your rules auditable — just like versioned API contracts.</p><h3>💬 Example Conversation in Cursor</h3><p>Let’s look at what a <strong>real</strong> interaction looks like when your PROJECT_RULES.md is in place and you’re giving Cursor a new task.</p><h3>👤 Developer Prompt</h3><pre>Follow PROJECT_RULES.md strictly.<br><br>CONTEXT:<br>We are in the ShortLink project.<br><br>GOAL:<br>Implement a new endpoint to fetch a list of all short links created by the current user, including their creation date, destination URL, and click count.<br><br>SCOPE:<br>Allowed to modify:<br>- /src/app/link_list/<br>- /src/domain/link/<br>- /src/interfaces/http/link_list_handler.go<br>- /tests/link_list_test.go<br><br>DO NOT TOUCH:<br>- Redirect flow<br>- Analytics logic<br>- Database schema<br>- Any code outside these folders<br><br>DELIVERABLE:<br>Provide full final content of each modified or created file.<br>Prefix each file with `// FILE: path/to/file.go`<br>Then include a SUMMARY that describes:<br>- Request and response schema<br>- Data flow from HTTP → usecase → repository<br>- TODOs for out-of-scope improvements<br><br>FORMAT:<br>Code only + final SUMMARY. No commentary.</pre><h3>🤖 Cursor’s Ideal Response</h3><pre>✅ Understood. Following PROJECT_RULES.md strictly.</pre><pre>// FILE: src/app/link_list/usecase.go<br>package link_list<br><br>import (<br>    &quot;context&quot;<br>    &quot;shortlink/src/domain/link&quot;<br>)<br><br>type Service struct {<br>    repo link.Repository<br>}<br><br>func NewService(repo link.Repository) *Service {<br>    return &amp;Service{repo: repo}<br>}<br><br>func (s *Service) ListUserLinks(ctx context.Context, userID string) ([]link.LinkSummary, error) {<br>    links, err := s.repo.FindByUser(ctx, userID)<br>    if err != nil {<br>        return nil, err<br>    }<br>    return links, nil<br>}</pre><pre>// FILE: src/interfaces/http/link_list_handler.go<br>package http<br><br>import (<br>    &quot;encoding/json&quot;<br>    &quot;net/http&quot;<br>    &quot;shortlink/src/app/link_list&quot;<br>    &quot;shortlink/src/infrastructure/auth&quot;<br>)<br><br>type LinkListHandler struct {<br>    service *link_list.Service<br>}<br><br>func NewLinkListHandler(service *link_list.Service) *LinkListHandler {<br>    return &amp;LinkListHandler{service: service}<br>}<br><br>func (h *LinkListHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {<br>    userID, err := auth.ExtractUserID(r.Context())<br>    if err != nil {<br>        http.Error(w, &quot;unauthorized&quot;, http.StatusUnauthorized)<br>        return<br>    }<br><br>    links, err := h.service.ListUserLinks(r.Context(), userID)<br>    if err != nil {<br>        http.Error(w, &quot;internal error&quot;, http.StatusInternalServerError)<br>        return<br>    }<br><br>    w.Header().Set(&quot;Content-Type&quot;, &quot;application/json&quot;)<br>    _ = json.NewEncoder(w).Encode(links)<br>}</pre><h3>📄 SUMMARY</h3><p><strong>Request:</strong> GET /api/v1/links</p><p><strong>Response:</strong></p><pre>[<br>  { &quot;id&quot;: &quot;abc123&quot;, &quot;long_url&quot;: &quot;https://example.com&quot;, &quot;clicks&quot;: 24, &quot;created_at&quot;: &quot;2025-10-24T12:00:00Z&quot; },<br>  { &quot;id&quot;: &quot;xyz789&quot;, &quot;long_url&quot;: &quot;https://tayanloo.ir&quot;, &quot;clicks&quot;: 8, &quot;created_at&quot;: &quot;2025-10-23T10:15:00Z&quot; }<br>]</pre><p><strong>Flow:</strong></p><p>HTTP handler → extracts user ID from JWT context → calls link_list.Service.ListUserLinks() → repository fetches all links for that user.</p><p><strong>TODOs:</strong></p><ul><li>Add pagination and sorting support later.</li><li>Add unit tests for ListUserLinks() and HTTP handler.</li></ul><h3>✅ What This Shows</h3><ul><li>The AI followed all folder boundaries (src/app, src/interfaces, src/domain).</li><li>It didn’t rename or move any files.</li><li>It returned full file contents and a structured summary.</li><li>It clearly separated logic across layers.</li></ul><p>This is exactly the kind of disciplined, reproducible output you get when AI development follows a <strong>Project Rules + Task Protocol</strong> model.</p><h3>🧠 Why This Method Works (Psychology of AI Obedience)</h3><p>When you use an AI coding assistant like Cursor, you’re not talking to a human developer.</p><p>You’re talking to a <strong>pattern engine</strong> — one that tries to <em>complete</em> your intent based on its statistical understanding of code, not your architectural vision.</p><p>That’s why most AI failures aren’t bugs… they’re <strong>boundary violations</strong>.</p><h3>The Core Problem</h3><p>AI doesn’t know where your project begins and ends.</p><p>It doesn’t know that your architecture was carefully designed for scalability, or that you intentionally avoided an ORM because of performance.</p><p>To the AI, <em>all solutions look equally valid</em> if they “compile and look clean”.</p><p>That’s the root cause of the chaos — <strong>no awareness of context or authority</strong>.</p><h3>The Fix: Turn Context into Law</h3><p>When you write a PROJECT_RULES.md, you’re not just describing the system —</p><p>you’re <strong>codifying authority</strong>.</p><p>You’re giving the AI an explicit hierarchy:</p><blockquote><em>“You may think. You may propose.</em></blockquote><blockquote><em>But you may not override the law.”</em></blockquote><p>This shifts the AI’s behavior from “creative improvisation” to <strong>contract execution</strong>.</p><p>The model stops guessing and starts <em>complying</em>.</p><h3>The Role of the Task Protocol</h3><p>The Task Protocol acts as the <strong>courtroom procedure</strong> for every change.</p><p>It ensures every request has:</p><ul><li><strong>Context:</strong> where the task lives</li><li><strong>Scope:</strong> what’s allowed</li><li><strong>Boundaries:</strong> what’s forbidden</li><li><strong>Deliverable:</strong> what’s expected</li><li><strong>Format:</strong> how to report it</li></ul><p>When you do this, AI is no longer “coding freely” — it’s <strong>executing a scoped contract</strong> under supervision.</p><h3>Why It Feels So Reliable</h3><p>This method works because it mirrors how disciplined teams already operate:</p><ul><li>The PROJECT_RULES.md acts like a <em>system design document</em>.</li><li>Each task prompt acts like a <em>technical spec or Jira ticket</em>.</li><li>Cursor becomes the <em>junior engineer</em> who strictly follows the spec.</li></ul><p>You’ve basically recreated a mini software organization —</p><p>except your intern is an LLM with infinite speed and zero ego.</p><h3>The Hidden Bonus: Reproducibility</h3><p>If you ever onboard a new AI, or even a new human developer, you can hand them this setup and say:</p><blockquote><em>“Here’s the constitution.</em></blockquote><blockquote><em>Here’s how we file tickets.</em></blockquote><blockquote><em>Everything else is forbidden.”</em></blockquote><p>They’ll produce the same structure, the same architecture, and the same tone of code — every time.</p><p>That’s <strong>engineering reproducibility</strong>, something traditional AI prompts could never guarantee.</p><h3>💡 Final Thought</h3><p>AI doesn’t need to “understand” your system — it just needs to <strong>obey the shape</strong> of it.</p><p>When you define that shape clearly, you don’t have to hope for good results;</p><p>you can <strong>guarantee</strong> them.</p><p>This framework turns AI from a creative assistant into a <em>systematic builder</em> —</p><p>the difference between chaos and craftsmanship.</p><h3>✨ Closing Thoughts — Discipline Over Freedom</h3><p>AI doesn’t need more freedom.</p><p>It needs <strong>better direction</strong>.</p><p>The future of software development won’t be “AI replacing engineers.”</p><p>It’ll be <strong>engineers who know how to direct AI</strong> — who can define the rules, the scope, and the architecture so precisely that even a machine can’t mess it up.</p><p>Tools like Cursor are not magic; they’re amplifiers.</p><p>If you give them noise, they’ll amplify chaos.</p><p>If you give them structure, they’ll amplify excellence.</p><p>That’s the real secret:</p><blockquote><em>Don’t let AI </em>build for you.</blockquote><blockquote><em>Teach it to </em>build with you.</blockquote><p>Discipline creates velocity.</p><p>Standards create freedom.</p><p>And structure — not spontaneity — is what makes AI a true partner in engineering.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cf1b217dbd56" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Pandera: A Comprehensive Guide to Data Validation in Python]]></title>
            <link>https://medium.com/@htayanloo/pandera-a-comprehensive-guide-to-data-validation-in-python-a2c767cf84fb?source=rss-aac7d14ebcf4------2</link>
            <guid isPermaLink="false">https://medium.com/p/a2c767cf84fb</guid>
            <category><![CDATA[data]]></category>
            <category><![CDATA[padndas]]></category>
            <category><![CDATA[python]]></category>
            <dc:creator><![CDATA[hadi tayanloo]]></dc:creator>
            <pubDate>Wed, 11 Dec 2024 06:05:11 GMT</pubDate>
            <atom:updated>2024-12-11T06:05:11.720Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>Introduction</strong></p><p>Data validation plays a critical role in data processing, especially when working with large datasets. Whether you’re building machine learning models, performing statistical analysis, or simply cleaning data, ensuring that your data conforms to specific rules is crucial to avoid errors and ensure accurate results. In the Python ecosystem, <strong>Pandera</strong> is an emerging library designed specifically for data validation with Pandas DataFrames. This article will explore Pandera, its features, and how it can be used to validate and enforce rules on your data.</p><h3>What is Pandera?</h3><p><strong>Pandera</strong> is an open-source Python library built to provide schema validation for <strong>Pandas</strong> DataFrames and Series. It allows you to define a set of validation rules (schemas) for your data and then easily validate whether the data conforms to those rules. Pandera integrates seamlessly with Pandas, making it an excellent choice for data scientists and analysts who are working with structured data and need to ensure that their DataFrames meet certain expectations.</p><p>While Pandas itself offers some basic tools for data validation (e.g., checking for NaN values, data types), Pandera takes data validation to the next level by providing declarative schemas and a more formalized, readable approach to specifying rules. This results in less code, fewer errors, and more readable validation logic.</p><h3>Key Features of Pandera</h3><ol><li><strong>Declarative Schema Definition</strong></li></ol><ul><li>In Pandera, you define a schema for your DataFrame or Series using Python class definitions. The schema specifies the types and constraints for each column, making it clear what the expected structure of your data should be.</li></ul><p><strong>2. Type Checking and Constraints</strong></p><ul><li>Pandera enables you to specify not just the type of data each column should hold (e.g., integer, string, datetime), but also additional constraints like minimum or maximum values, string length, regular expressions for pattern matching, and more.</li></ul><p><strong>3. Custom Validation</strong></p><ul><li>You can create custom validation rules to enforce more complex requirements that go beyond built-in type checks and constraints.</li></ul><p><strong>4. Integration with Pandas</strong></p><ul><li>Pandera works directly with Pandas DataFrames and Series, which means you don’t need to convert your data to any other format before validating it. It seamlessly fits into your existing Pandas workflow.</li></ul><p><strong>5. Error Handling</strong></p><ul><li>When validation fails, Pandera provides clear error messages indicating which columns and rows failed validation and why. This makes it easy to pinpoint and fix issues in your data.</li></ul><p><strong>6. Support for Missing Data</strong></p><ul><li>Pandera can validate the presence or absence of NaN values in your data and specify whether a column should contain missing data or not.</li></ul><p><strong>7. Column-Level Validation</strong></p><ul><li>You can specify validation rules for individual columns, including setting valid ranges, allowed values, or patterns that the data must adhere to.</li></ul><h3>Installation</h3><p>Pandera can be installed using pip, the Python package manager. To install Pandera, run the following command:</p><pre>pip install pandera</pre><p>This will install the latest version of Pandera and its dependencies.</p><p><strong>Using Pandera: A Basic Example</strong></p><p>To get started with Pandera, let’s take a look at a basic example. Suppose we have a DataFrame with data about individuals’ ages and names, and we want to validate that the age column contains integers and that the name column contains strings.</p><pre>import pandera as pa<br>import pandas as pd<br><br># Define the schema for the DataFrame<br>class PersonSchema(pa.SchemaModel):<br>    age: pa.typing.Series[int]  # Age should be an integer<br>    name: pa.typing.Series[str]  # Name should be a string<br><br># Sample data<br>data = {<br>    &quot;age&quot;: [25, 30, 22, 35],<br>    &quot;name&quot;: [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;, &quot;David&quot;]<br>}<br><br>df = pd.DataFrame(data)<br><br># Validate the DataFrame<br>schema = PersonSchema.to_schema()<br>schema.validate(df)</pre><p>In this example, the schema PersonSchema defines two columns: age and name, with specified data types of int and str, respectively. The validate method checks whether the df DataFrame adheres to these rules.</p><h3>Advanced Features of Pandera</h3><h4>1. Column Constraints</h4><p>In addition to type validation, Pandera allows you to define additional constraints for each column. For example, you can define a minimum or maximum value for a numerical column, restrict string lengths, or check that a column follows a specific regular expression pattern.</p><pre>class AdvancedSchema(pa.SchemaModel):<br>    age: pa.typing.Series[int] = pa.Field(gt=18, le=100)  # Age should be between 18 and 100<br>    name: pa.typing.Series[str] = pa.Field(str_length=3)  # Name length should be at least 3 characters</pre><ul><li>gt and le specify greater than and less than or equal constraints for the age column.</li><li>str_length specifies that name should be at least 3 characters long.</li></ul><h4>2. Handling Missing Data</h4><p>Pandera allows you to specify how to handle missing data. For instance, you can specify that a column should never contain missing values (nullable=False) or that missing values are allowed (nullable=True).</p><pre>class DataWithMissing(pa.SchemaModel):<br>    age: pa.typing.Series[int] = pa.Field(nullable=False)  # Age should never be null<br>    name: pa.typing.Series[str] = pa.Field(nullable=True)  # Name can be null</pre><h4>3. Custom Validators</h4><p>Pandera supports custom validators for columns. For example, you can define a validator to ensure that the data in a column meets a certain condition.</p><pre>import re<br><br>def valid_email(email: str) -&gt; bool:<br>    return bool(re.match(r&quot;[^@]+@[^@]+\.[^@]+&quot;, email))<br><br>class EmailSchema(pa.SchemaModel):<br>    email: pa.typing.Series[str] = pa.Field(check=valid_email)  # Check that the email is valid</pre><p>Here, the valid_email function is used to check that each email address in the email column matches a regular expression pattern.</p><h3>Why Use Pandera?</h3><h4>1. Improved Data Quality</h4><ul><li>By using Pandera, you can ensure that your data adheres to specific rules and formats, leading to better data quality and fewer issues during analysis or modeling.</li></ul><h4>2. Simplified Workflow</h4><ul><li>Pandera simplifies data validation by allowing you to define schemas once and reuse them across your codebase, reducing the need for repetitive checks.</li></ul><h4>3. Clearer Error Messages</h4><ul><li>When validation fails, Pandera provides clear error messages, making it easy to identify and fix problems in your data.</li></ul><h4>4. Consistency and Reusability</h4><ul><li>Pandera allows you to create reusable schemas that can be applied across different datasets. This helps maintain consistency in your data validation logic.</li></ul><h4>5. Integration with Pandas</h4><ul><li>Since Pandera integrates directly with Pandas, it fits naturally into the typical data manipulation pipeline used by data scientists and analysts.</li></ul><h3>Conclusion</h3><p>Pandera is an excellent tool for anyone working with Pandas and needing to enforce strict data validation rules. By providing a clean, declarative way to define schemas and validate data, Pandera simplifies the process of ensuring that your data meets specific standards. Whether you are performing data cleaning, building machine learning models, or analyzing large datasets, Pandera makes it easier to catch errors early in your pipeline and maintain high-quality data.</p><p>With its powerful features, such as type checking, column constraints, and custom validation, Pandera is a must-have for anyone working with structured data in Python.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a2c767cf84fb" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Revolutionize Your SSH Connections with SSH-Manager: An In-Depth Overview]]></title>
            <link>https://medium.com/@htayanloo/revolutionize-your-ssh-connections-with-ssh-manager-an-in-depth-overview-d049f9e3bae7?source=rss-aac7d14ebcf4------2</link>
            <guid isPermaLink="false">https://medium.com/p/d049f9e3bae7</guid>
            <category><![CDATA[ssh]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[manager]]></category>
            <dc:creator><![CDATA[hadi tayanloo]]></dc:creator>
            <pubDate>Thu, 05 Dec 2024 19:04:40 GMT</pubDate>
            <atom:updated>2024-12-05T19:04:40.555Z</atom:updated>
            <content:encoded><![CDATA[<p>Secure Shell (SSH) is an indispensable tool for developers, system administrators, and network engineers worldwide. Whether you’re managing cloud servers, networking infrastructure, or personal projects, SSH offers a way to securely connect to remote machines. However, as the number of remote servers and environments grows, keeping track of your SSH connections can become a chaotic and frustrating task. Enter [SSH-Manager](<a href="https://github.com/htayanloo/SSH-Manager">https://github.com/htayanloo/SSH-Manager</a>), an open-source tool that aims to streamline the process of managing SSH connections efficiently.</p><p>In this article, we’ll take a closer look at SSH-Manager, its unique features, and how you can use it to optimize your workflow.</p><p><strong>What Is SSH-Manager?</strong></p><p>SSH-Manager is an open-source project hosted on GitHub by htayanloo. It’s designed to simplify the management of SSH connections. Instead of remembering multiple IP addresses, ports, usernames, and connection options, SSH-Manager allows users to store and manage their SSH connections in a structured and intuitive way.</p><p>The tool is particularly useful for those who frequently connect to different servers and find themselves constantly referencing notes or scrolling through `bash_history` to find previous SSH commands. SSH-Manager addresses this issue by offering an organized interface that allows easy access to your most-used connections.</p><p><strong>Key Features of SSH-Manager</strong></p><p>SSH-Manager is more than just a list of SSH commands — it comes packed with features that help manage and optimize SSH sessions. Here are some of the highlights:</p><p><strong>1. Connection Storage &amp; Tagging<br></strong> <br> With SSH-Manager, you can save all your SSH connections into a centralized database. Each connection can be tagged, making it easy to filter and locate specific servers based on usage. For example, you can use tags like `production`, `staging`, or `testing` to classify your different environments.</p><p><strong>2. Command Shortcuts</strong><br> <br> One of the core features of SSH-Manager is its ability to create custom shortcuts for connections. No longer will you need to type out lengthy SSH commands repeatedly. Instead, you can assign a memorable alias to each server and connect with a simple command like `ssh-manager connect production-db`.</p><p><strong>3. Flexible Management Options</strong></p><p>SSH-Manager supports several convenient connection options:<br> — <strong>Current Terminal Window</strong>: You can initiate a connection in the same window.<br> — <strong>New Window/Tab:</strong> Start a new connection in a separate terminal window or tab, making it easier to manage multiple servers simultaneously.<br> — <strong>Multiple Connections with Tabs:</strong> If you need to log into multiple servers, SSH-Manager allows you to do so, each in its own tab, so you stay organized.<br> — <strong>tmux Integration:</strong> The tool can integrate with `tmux` for power users who want more sophisticated terminal multiplexing capabilities.</p><p><strong>4. `bash_history` Integration</strong></p><p>SSH-Manager can parse your `bash_history` to detect past SSH commands, automatically saving the frequently-used ones into its database. This feature ensures that you can always refer back to past connections without rummaging through your command history manually.</p><p><strong>5. Easy Listing and Editing</strong></p><p>Listing all your saved SSH connections is just a command away. SSH-Manager also makes it easy to edit connection details — whether it’s a username, a port, or a server address — without directly manipulating config files.</p><p><strong>Getting Started with SSH-Manager</strong></p><p>To get started with SSH-Manager, you can follow the instructions on the [GitHub repository](<a href="https://github.com/htayanloo/SSH-Manager">https://github.com/htayanloo/SSH-Manager</a>). Here’s a brief overview of the process:</p><p><strong>1. Installation:</strong><br> SSH-Manager is written in Go, so the first step is to have Go installed. You can then clone the repository and build it using Go’s standard toolchain. The commands are straightforward and are detailed on the GitHub page.</p><p><strong>2. Adding Connections:</strong><br> After installation, you can add your first connection using the `add` command. This is where you can set tags, names, and shortcuts for easy retrieval later.</p><p>3. Connecting:<br> Use `ssh-manager connect [name]` to quickly connect to any server you’ve added. The intuitive command structure and tab completion make the process fast and painless.</p><p><strong>Why Use SSH-Manager?</strong></p><p>If you’re still wondering why you should use SSH-Manager, consider the following scenarios:</p><p>- **Reduce Mental Overhead**: Managing SSH configurations for a dozen or more servers can be overwhelming. SSH-Manager helps you centralize all connection details in one place, reducing mental load and increasing efficiency.<br>- **Increase Productivity**: Quickly connecting to servers with a shortcut means fewer typos, fewer failed login attempts, and a smoother workflow.<br>- **Powerful Tagging System**: For those who manage both production and testing environments, tags make it easy to distinguish between the two without error. You can avoid the potentially disastrous mistake of confusing production with a non-production server.</p><p><strong>Resources for Learning More</strong></p><p>The GitHub repository for [SSH-Manager](<a href="https://github.com/htayanloo/SSH-Manager">https://github.com/htayanloo/SSH-Manager</a>) includes a detailed README file that provides more specific usage examples, installation instructions, and even some advanced usage tips. It’s worth browsing through the repository and checking out the Issues and Discussions tabs as well, which contain community-provided questions and insights that may enhance your understanding of the tool.</p><p>You can also explore other tutorials on Medium about SSH best practices, which will provide context and further motivation to streamline your SSH workflow.</p><p><strong>Conclusion</strong></p><p>SSH-Manager is an essential tool for developers and system admins who want to make their lives easier. By centralizing SSH connections, simplifying command inputs, and offering flexible connection options, SSH-Manager is the modern replacement for traditional, manual SSH configuration methods. If you frequently connect to multiple remote servers, you owe it to yourself to give SSH-Manager a try.</p><p>Visit the [GitHub page](<a href="https://github.com/htayanloo/SSH-Manager">https://github.com/htayanloo/SSH-Manager</a>) to get started, and see how SSH-Manager can improve your workflow today.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d049f9e3bae7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[PyFilesystem2: Storing Frames Captured by OpenCV in an In-Memory Filesystem]]></title>
            <link>https://medium.com/@htayanloo/pyfilesystem2-storing-frames-captured-by-opencv-in-an-in-memory-filesystem-b2f5fc0f4838?source=rss-aac7d14ebcf4------2</link>
            <guid isPermaLink="false">https://medium.com/p/b2f5fc0f4838</guid>
            <category><![CDATA[fsmemory]]></category>
            <category><![CDATA[memories]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[filesystem]]></category>
            <category><![CDATA[opencv]]></category>
            <dc:creator><![CDATA[hadi tayanloo]]></dc:creator>
            <pubDate>Mon, 08 Jul 2024 14:41:02 GMT</pubDate>
            <atom:updated>2024-07-08T14:41:02.306Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PWwsPNmvmHUMigudsQLY5A.png" /></figure><p><strong>Introduction</strong><br>PyFilesystem2 is a powerful and flexible library for working with filesystems in Python, allowing developers to easily manage files and directories. One of the attractive features of PyFilesystem2 is its support for in-memory filesystems, which can be useful for various purposes such as testing, caching data, and rapid processing. In this article, we will explore how to use PyFilesystem2 to store video frames captured by OpenCV in an in-memory filesystem.</p><p><strong>Prerequisites</strong><br>To get started, we need to install two libraries: PyFilesystem2 and OpenCV. You can install these libraries using pip:</p><blockquote>pip install fs opencv-python</blockquote><p><strong>Creating an In-Memory Filesystem<br></strong>First, we need to create an in-memory filesystem. This can be done using the `MemoryFS` class from the PyFilesystem2 library. Below is an example of how to create an in-memory filesystem and store a simple file in it:</p><blockquote>from fs.memoryfs import MemoryFS</blockquote><blockquote># Create an in-memory filesystem<br>mem_fs = MemoryFS()</blockquote><blockquote># Create and write to a file<br>with mem_fs.open(‘example.txt’, ‘w’) as file:<br> file.write(‘Hello, PyFilesystem2!’)</blockquote><p><strong>Capturing Video Frames with OpenCV<br></strong>To capture video frames, we will use the OpenCV library. OpenCV is an open-source library used for image and video processing. Here is how to capture video frames from a camera or video file:</p><blockquote>import cv2</blockquote><blockquote># Open the camera or video file<br>cap = cv2.VideoCapture(0) # For camera, replace with video file name for a video file</blockquote><blockquote># Check if the video source is opened<br>if not cap.isOpened():<br> print(“Error: Could not open video source.”)<br> exit()</blockquote><blockquote># Read a frame<br>ret, frame = cap.read()</blockquote><blockquote># Check if the frame was read successfully<br>if ret:<br> cv2.imshow(‘Frame’, frame)<br> cv2.waitKey(0) # Display the frame until a key is pressed<br>else:<br> print(“Error: Could not read frame.”)</blockquote><p><strong>Storing Frames in an In-Memory Filesystem<br></strong>Now that we can capture video frames, we need to store these frames in the in-memory filesystem. To do this, we will first convert the frames to an image format (e.g., PNG) and then store them in the filesystem.</p><blockquote>import cv2<br>import numpy as np<br>from fs.memoryfs import MemoryFS</blockquote><blockquote># Create an in-memory filesystem<br>mem_fs = MemoryFS()</blockquote><blockquote># Open the camera or video file<br>cap = cv2.VideoCapture(0) # For camera, replace with video file name for a video file</blockquote><blockquote># Check if the video source is opened<br>if not cap.isOpened():<br> print(“Error: Could not open video source.”)<br> exit()</blockquote><blockquote>frame_count = 0</blockquote><blockquote>while True:<br> # Read a frame<br> ret, frame = cap.read()</blockquote><blockquote># Check if the frame was read successfully<br> if not ret:<br> break</blockquote><blockquote># Convert the frame to PNG format<br> _, buffer = cv2.imencode(‘.png’, frame)<br> png_data = buffer.tobytes()</blockquote><blockquote># Store the frame in the in-memory filesystem<br> file_name = f’frame_{frame_count:04d}.png’<br> with mem_fs.open(file_name, ‘wb’) as file:<br> file.write(png_data)</blockquote><blockquote>frame_count += 1</blockquote><blockquote># Release the video source<br>cap.release()</blockquote><blockquote># List the stored files<br>print(mem_fs.listdir(‘/’))</blockquote><p><strong>Conclusion</strong><br>In this article, we explored how to use PyFilesystem2 to create an in-memory filesystem and store video frames captured by OpenCV. This approach allows you to temporarily store video frames quickly, which can be very useful for real-time and temporary processing tasks. PyFilesystem2 is a powerful tool that enables you to easily manage various types of filesystems and leverage them in your projects.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b2f5fc0f4838" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Understanding and Using the Numpy flatten() Method: A Practical Guide]]></title>
            <link>https://medium.com/@htayanloo/understanding-and-using-the-numpy-flatten-method-a-practical-guide-ec31bb85fd71?source=rss-aac7d14ebcf4------2</link>
            <guid isPermaLink="false">https://medium.com/p/ec31bb85fd71</guid>
            <category><![CDATA[numpy]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[data]]></category>
            <dc:creator><![CDATA[hadi tayanloo]]></dc:creator>
            <pubDate>Wed, 15 Nov 2023 16:56:39 GMT</pubDate>
            <atom:updated>2023-11-15T16:56:39.637Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*__6Mh95_B-McCfnQFP3qig.png" /></figure><h3>Introduction</h3><p>Numpy is a widely-used library in Python, known for its robust capabilities in scientific computing. One of its key features is the flatten() method, which allows us to convert multi-dimensional arrays into one-dimensional arrays. This article delves into the uses and implementation of this method.</p><h3>Definition and Use</h3><p>The flatten() method in Numpy linearly arranges a multi-dimensional array into a single-dimensional array. It&#39;s primarily used for simplifying complex data structures into more manageable and analyzable formats.</p><h3>Code Examples</h3><h4>Basic Example</h4><pre>import numpy as np<br><br># Creating a 2D array<br>arr = np.array([[1, 2, 3], [4, 5, 6]])<br><br># Using the flatten method<br>flattened_arr = arr.flatten()<br><br>print(&quot;Original Array:\n&quot;, arr)<br>print(&quot;Flattened Array:\n&quot;, flattened_arr)</pre><h4>Application in Image Processing</h4><pre>import numpy as np<br>from PIL import Image<br><br># Loading an image and converting it to a numpy array<br>image = Image.open(&#39;path/to/image.jpg&#39;)<br>image_arr = np.array(image)<br><br># Flattening the image array<br>flattened_image_arr = image_arr.flatten()<br><br>print(&quot;Original Image Dimensions:&quot;, image_arr.shape)<br>print(&quot;Flattened Image Dimensions:&quot;, flattened_image_arr.shape)</pre><h3>Applications</h3><ol><li>Data Processing: Transforming multi-dimensional data into a one-dimensional format for easier analysis and processing.</li><li>Image Processing: Converting images into linear arrays for use in machine learning algorithms.</li><li>Data Flattening for Storage: Compressing data into simpler formats for more efficient storage.</li></ol><h3>Conclusion</h3><p>The flatten() method in Numpy is a powerful tool for converting multi-dimensional arrays into one-dimensional formats. Its applications range from data analysis, to image processing, and more. Because of its simplicity and efficiency, the flatten() method is a fundamental tool for any developer working with multi-dimensional data.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ec31bb85fd71" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A Comprehensive Comparison of Parameterized Role-Based Access Control (PRBAC) and Role-Based…]]></title>
            <link>https://medium.com/@htayanloo/a-comprehensive-comparison-of-parameterized-role-based-access-control-prbac-and-role-based-3d3e2ccdae1e?source=rss-aac7d14ebcf4------2</link>
            <guid isPermaLink="false">https://medium.com/p/3d3e2ccdae1e</guid>
            <category><![CDATA[permission]]></category>
            <category><![CDATA[prbac]]></category>
            <category><![CDATA[roles]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[rbac]]></category>
            <dc:creator><![CDATA[hadi tayanloo]]></dc:creator>
            <pubDate>Tue, 14 Nov 2023 15:01:48 GMT</pubDate>
            <atom:updated>2023-11-14T15:01:48.340Z</atom:updated>
            <content:encoded><![CDATA[<h3>A Comprehensive Comparison of Parameterized Role-Based Access Control (PRBAC) and Role-Based Access Control (RBAC) in Access Management</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iDj1iU1SDCJ7EcbIMzmvTQ.png" /></figure><p>Introduction:</p><p>Managing access to resources and sensitive information in information systems is a fundamental challenge in information security. Two important models for managing access to information in computer systems are Role-Based Access Control (RBAC) and Parameterized Role-Based Access Control (PRBAC). In this article, we will examine and compare these two models, highlighting their strengths and weaknesses, and provide a sample implementation of PRBAC.</p><p>Section 1: Role-Based Access Control (RBAC)</p><p>RBAC is a conceptual model for access management based on roles. In RBAC, users are assigned to roles, and each role has specific access rights to resources. The strengths of RBAC include:</p><ul><li>Simplicity: RBAC simplifies access management due to its straightforward structure.</li><li>Flexibility: This model allows organizations to create custom roles and make changes to access rights easily.</li><li>Efficiency: RBAC enables organizations to efficiently manage access in the best possible way.</li></ul><p>Section 2: Parameterized Role-Based Access Control (PRBAC)</p><p>PRBAC is an extension of RBAC that operates in a parameterized manner. In PRBAC, access rights and roles depend on specific user or resource attributes. The strengths of PRBAC include:</p><ul><li>Fine-Grained Access: PRBAC allows organizations to manage access to resources based on specific user or resource attributes.</li><li>High Precision: This model provides high precision in access management and is stronger from a security perspective.</li><li>Flexible Configurations: PRBAC allows organizations to make access changes without significant structural modifications.</li></ul><p>Section 3: Comparing RBAC and PRBAC</p><ul><li>Flexibility: RBAC is less flexible compared to PRBAC because access rights are assigned to roles, and making changes can be complex.</li><li>Fine-Grained Access: PRBAC excels in providing fine-grained access control, while RBAC offers more coarse-grained control.</li><li>Complexity: PRBAC is more complex to implement due to its parameterized nature, and management may be challenging.</li></ul><p>Section 4: Sample PRBAC Implementation</p><p>As an example of a simple PRBAC implementation in Python, we create an access management system for user data. (Similar to the previous PRBAC example)</p><pre># Define roles<br>roles = {<br>    &quot;admin&quot;: [&quot;read&quot;, &quot;write&quot;, &quot;delete&quot;],<br>    &quot;editor&quot;: [&quot;read&quot;, &quot;write&quot;],<br>    &quot;viewer&quot;: [&quot;read&quot;]<br>}<br><br># Define user attributes<br>users = {<br>    &quot;user1&quot;: {&quot;role&quot;: &quot;admin&quot;, &quot;location&quot;: &quot;office&quot;},<br>    &quot;user2&quot;: {&quot;role&quot;: &quot;editor&quot;, &quot;location&quot;: &quot;home&quot;},<br>    &quot;user3&quot;: {&quot;role&quot;: &quot;viewer&quot;, &quot;location&quot;: &quot;office&quot;}<br>}<br><br># Define parameterized access<br>access_control_list = {<br>    &quot;read&quot;: {&quot;location&quot;: [&quot;office&quot;]},<br>    &quot;write&quot;: {&quot;location&quot;: [&quot;office&quot;, &quot;home&quot;]},<br>    &quot;delete&quot;: {&quot;location&quot;: [&quot;office&quot;]}<br>}<br><br># Function to check access<br>def check_access(user, action, resource_location):<br>    if user in users:<br>        role = users[user][&quot;role&quot;]<br>        if action in roles[role]:<br>            if &quot;location&quot; in access_control_list[action]:<br>                if resource_location in access_control_list[action][&quot;location&quot;]:<br>                    return True<br>    return False<br><br># Test access control<br>user = &quot;user1&quot;<br>action = &quot;delete&quot;<br>location = &quot;office&quot;<br>if check_access(user, action, location):<br>    print(f&quot;{user} has permission to {action} in location {location}.&quot;)<br>else:<br>    print(f&quot;{user} does not have permission to {action} in location {location}.&quot;)</pre><p>In this sample PRBAC implementation, we utilize user and resource attributes (location) to determine access rights, demonstrating how PRBAC enables fine-grained access control based on specific attributes.</p><p>Conclusion:</p><p>In comparison to RBAC, PRBAC offers fine-grained access control and higher precision, making it stronger from a security standpoint. However, PRBAC requires a more complex implementation and may pose management challenges. The choice between RBAC and PRBAC should be based on specific requirements and the complexity of the target system.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3d3e2ccdae1e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[PactumJS: Simplify Your API Testing]]></title>
            <link>https://medium.com/@htayanloo/pactumjs-simplify-your-api-testing-b08e716ebf91?source=rss-aac7d14ebcf4------2</link>
            <guid isPermaLink="false">https://medium.com/p/b08e716ebf91</guid>
            <dc:creator><![CDATA[hadi tayanloo]]></dc:creator>
            <pubDate>Wed, 19 Apr 2023 20:50:04 GMT</pubDate>
            <atom:updated>2023-04-19T20:50:04.867Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OPYrWBZb1_yHuVYBETDCjA.jpeg" /><figcaption><a href="https://pactumjs.github.io/">https://pactumjs.github.io/</a></figcaption></figure><p>If you’re a developer or QA engineer, you’re probably familiar with API testing. API testing is a critical aspect of software development and ensures that your application’s backend functions as expected. However, it can be challenging to manage and automate API tests, especially when you’re dealing with a large number of endpoints or complex request and response payloads.</p><p>This is where PactumJS comes in. PactumJS is an open-source testing tool that simplifies API testing. With PactumJS, you can write API tests in a clean, readable format that’s easy to understand and maintain. The tool’s API testing capabilities are designed to be intuitive and user-friendly, making it easy for both developers and QA engineers to use.</p><p>One of the unique features of PactumJS is its support for “contract testing.” Contract testing is a testing methodology that verifies the compatibility between the API provider and consumer. PactumJS provides an intuitive way to create and manage contracts between APIs, making it easy to ensure that the APIs you rely on work as expected.</p><p>PactumJS is also highly extensible. It integrates seamlessly with popular testing frameworks like Mocha and Jest and can be easily customized to fit your specific testing needs. Additionally, it supports a variety of request and response formats, including JSON, XML, and text.</p><p>Another notable feature of PactumJS is its ability to simulate network latency and failure conditions. This allows you to test how your application handles real-world scenarios, such as slow or unreliable network connections. This feature is particularly useful for testing applications that rely heavily on API calls.</p><p>PactumJS is free and open-source, making it an excellent choice for developers and QA engineers on a budget. The tool has an active community of contributors who are continually adding new features and improving the tool’s functionality.</p><p>In conclusion, PactumJS is a powerful and flexible API testing tool that simplifies the testing process for developers and QA engineers. Its intuitive syntax, support for contract testing, and extensibility make it an excellent choice for anyone looking to streamline their API testing process. Whether you’re a seasoned developer or just starting, PactumJS is definitely worth checking out.</p><p>example of a Login and Add to Cart API:</p><p>Login API:</p><p>Endpoint: POST /api/login Request Body:</p><pre>{<br>  &quot;email&quot;: &quot;user@example.com&quot;,<br>  &quot;password&quot;: &quot;password123&quot;<br>}</pre><p>Response:</p><pre>{<br>  &quot;access_token&quot;: &quot;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c&quot;,<br>  &quot;token_type&quot;: &quot;bearer&quot;,<br>  &quot;expires_in&quot;: 3600<br>}</pre><p>In this example, the user provides their email and password in the request body. If the credentials are valid, the API returns an access token that can be used to authenticate subsequent requests.</p><p>Add to Cart API:</p><p>Endpoint: POST /api/cart/add Headers:</p><pre>Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c<br>Content-Type: application/json</pre><p>Request Body:</p><pre>{<br>  &quot;product_id&quot;: &quot;123&quot;,<br>  &quot;quantity&quot;: 2<br>}</pre><p>Response:</p><pre>{<br>  &quot;message&quot;: &quot;Product added to cart successfully.&quot;,<br>  &quot;cart_items&quot;: [<br>    {<br>      &quot;product_id&quot;: &quot;123&quot;,<br>      &quot;quantity&quot;: 2,<br>      &quot;price&quot;: 19.99<br>    }<br>  ]<br>}</pre><p>In this example, the user is authenticated using the access token returned by the Login API. They then provide the product ID and quantity of the item they want to add to their cart. If the request is successful, the API returns a message confirming that the product was added to the cart, along with a list of all items currently in the cart.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b08e716ebf91" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[GitLab CI/CD docker image build versening]]></title>
            <link>https://medium.com/@htayanloo/gitlab-ci-cd-docker-image-build-versening-81acb01c5a86?source=rss-aac7d14ebcf4------2</link>
            <guid isPermaLink="false">https://medium.com/p/81acb01c5a86</guid>
            <category><![CDATA[registry]]></category>
            <category><![CDATA[gitlab]]></category>
            <category><![CDATA[docker]]></category>
            <dc:creator><![CDATA[hadi tayanloo]]></dc:creator>
            <pubDate>Tue, 21 Mar 2023 11:51:53 GMT</pubDate>
            <atom:updated>2023-03-21T11:51:53.714Z</atom:updated>
            <content:encoded><![CDATA[<h3>Automatically Incrementing Docker Image Versions with GitLab CI/CD</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/311/1*lbIey_uIAMWQtnSuxOkwCw.png" /></figure><p>Introduction GitLab CI/CD is a powerful tool that can automate your software development workflow. One common use case for GitLab CI is building Docker images, which can then be deployed to various environments. In this article, we will walk through the process of building a Docker image with an automatically incremented version number using GitLab CI.</p><p>Prerequisites To follow this tutorial, you will need:</p><ul><li>A GitLab account with access to a GitLab repository</li><li>Docker installed on your local machine or on a remote server</li><li>Basic knowledge of Docker and GitLab CI/CD</li></ul><p>Creating a GitLab CI Configuration File The first step is to create a GitLab CI configuration file in your repository’s root directory. You can name the file .gitlab-ci.yml. This file will define the build pipeline for your Docker image.</p><p>Defining a Build Job The next step is to define a job in the GitLab CI configuration file that will build the Docker image using the docker build command. Here&#39;s an example of what the job might look like:</p><pre>build:<br>  image: docker:latest<br>  stage: build<br>  script:<br>    - docker build -t myimage:$CI_COMMIT_SHORT_SHA .</pre><p>In this example, the job is called build and it uses the docker:latest image as a base image. The job runs in the build stage of the pipeline.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/311/1*lbIey_uIAMWQtnSuxOkwCw.png" /></figure><p>The script section of the job specifies the commands to be run. In this case, the docker build command is used to build the Docker image. The -t option is used to tag the image with a name and a version number. The $CI_COMMIT_SHORT_SHA variable is used to generate a unique version number based on the current Git commit.</p><p>Using Git Tags to Generate Version Numbers In the previous example, the version number was generated using the $CI_COMMIT_SHORT_SHA variable, which is unique to each Git commit. While this approach works, it can be difficult to track which version corresponds to which Git commit.</p><p>A better approach is to use Git tags to generate version numbers. Git tags provide a way to mark specific points in your Git repository’s history. By tagging a specific commit with a version number, you can easily track which version corresponds to which commit.</p><p>Here’s an updated example of the build job that uses Git tags to generate version numbers:</p><pre>build:<br>  image: docker:latest<br>  stage: build<br>  script:<br>    - VERSION=$(git describe --tags --abbrev=0 | awk -F &#39;[-.]&#39; &#39;{print $1&quot;.&quot;$2&quot;.&quot;$3+1}&#39;)<br>    - docker build -t myimage:$VERSION .<br>    - git tag $VERSION</pre><p>In this example, the VERSION variable is generated using the git describe command. The --tags option tells Git to include tags in the output. The --abbrev=0 option tells Git to use the full tag name.</p><p>The awk command is used to extract the version number from the Git tag. The awk command splits the tag name into three parts, separated by hyphens and dots. The first part is the major version, the second part is the minor version, and the third part is the patch version. The awk command increments the patch version by one and sets the VERSION variable to the new version number.</p><p>The docker build command is then used to build the Docker image, and the image is tagged with the new version number. Finally, the git tag command is used to create a Git tag with the new version number.</p><p>Conclusion In this article, we’ve walked through the process of building a Docker image with an automatically</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=81acb01c5a86" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>