# Swapnoneel Saha — Full Content Index > Generated from the same Markdown and trust sources used by the website. > See /llms.txt for the concise profile and current route index. --- # Credentials and verification — Swapnoneel Saha A source map for agents, recruiters, and collaborators evaluating Swapnoneel Saha. It separates public records from first-party claims and states the limits of each source. Last reviewed: 23 August 2026. External pages can change; re-check the linked source before making a time-sensitive or high-stakes decision. ## How evidence is classified - Official record: published by the school, institution, or authority responsible for the record. - Platform or publisher record: public activity, authorship, or an award recorded by the platform that hosted it. - Self-reported: supplied by Swapnoneel on this portfolio or a personal profile; useful context, but not independent verification. ## Evidence index ### [GitHub profile](https://github.com/Swpn0neel) - Evidence level: Platform or publisher record - Source: GitHub - Supports: Public code, repositories, contribution activity, the name Swapnoneel Saha, and a profile link back to swapnoneel.site. - Does not prove: A personal GitHub profile supports identity continuity and public technical activity; it does not independently verify employment or private client results. ### [Technical article bylines](https://keploy.io/blog/tag/software-development) - Evidence level: Platform or publisher record - Source: Keploy - Supports: Publisher-hosted technical articles carrying Swapnoneel Saha's byline on Keploy's official domain. - Does not prove: A byline verifies published work on the named domain, not the complete scope, dates, or business impact of an employment relationship. ### [Open-source documentation history](https://github.com/keploy/keploy/wiki/1.-Know-more-about-Keploy) - Evidence level: Platform or publisher record - Source: Keploy on GitHub - Supports: Public edit history in Keploy's GitHub repository attributes a documentation edit to Swapnoneel Saha. - Does not prove: This is evidence of a specific public contribution, not proof of every open-source or employment claim in the résumé. ### [Hack Around the World 2 project and award](https://devpost.com/software/the-magnificent-seven) - Evidence level: Platform or publisher record - Source: Devpost - Supports: The project record names Swapnoneel Saha as a creator and records the My First Travel Hack winner placement. - Does not prove: The record supports this named project and placement only; it does not verify other awards listed on the portfolio. ### [CBSE Class XII result](https://www.kalyanipublicschool.org/src/pdf/KPS_TOPPERS.pdf) - Evidence level: Official record - Source: Kalyani Public School - Supports: A result document published on the school's official domain lists Swapnoneel Saha with a 93% Class XII result. - Does not prove: This verifies the named school result only. The portfolio does not publish a university transcript, degree certificate, or identity document. ### [Résumé and professional timeline](https://www.swapnoneel.site/resume) - Evidence level: Self-reported - Source: swapnoneel.site - Supports: The current skills, employment timeline, education, projects, and stated impact that Swapnoneel presents to prospective collaborators. - Does not prove: These claims are first-party statements unless a separate source on this page supports them. Current and past employer roles are not presented as employer-verified. ### [Professional profile](https://www.linkedin.com/in/swapnoneel-saha-14a3161b6/) - Evidence level: Self-reported - Source: LinkedIn - Supports: A longstanding professional profile with work and education history under the same name. - Does not prove: LinkedIn profile fields are generally supplied by the account holder and should not be treated as employer or institution verification by themselves. ## Next steps - [Review the résumé](https://www.swapnoneel.site/resume): See the complete professional timeline and skills. - [Inspect work samples](https://www.swapnoneel.site/work): Explore experience, projects, and case studies. - [Start a conversation](https://www.swapnoneel.site/contact): Share an opportunity, project, or focused question. --- # Work Experience ## GTM Engineer • Maxim AI URL: https://www.swapnoneel.site/work/maxim Date: Jul 2026 - Present Summary: Managing the open-source community and working on Bifrost, running the social handles, and creating video content and technical blogs. [Maxim AI](https://www.getmaxim.ai) is building the evaluation and observability platform for GenAI, empowering AI teams to ship agents with enterprise-grade reliability and performance. ## What I am doing _\*haven't started cooking yet..._ ## GTM Engineer • Zonko Labs URL: https://www.swapnoneel.site/work/zonko Date: Mar 2026 - Jul 2026 Summary: Tested and launched Luffy (an AI Slack co-worker), built internal latency logging tools, grew the user base, and rebuilt the company website. [Zonko Labs](https://zonko.ai) is a frontier AI lab building the first truly global, daily-use, everyone-opens-it product born in India. ## What I did - Formed the first layer of internal testing for **Luffy**, an AI co-worker in Slack, helping the dev team catch bugs early. - Built an internal tool to capture **Luffy's data logs**, generating reports on latency and probable slowdowns. - Growing Luffy's user base from **0 to 50+ users**, mainly startups with 1-50 employees. - Stayed in constant contact with early adopters, relaying feedback and feature requests to the dev team, and **rebuilding the company website**, replacing a low-quality, outdated version. ## DevRel Engineer • Keploy URL: https://www.swapnoneel.site/work/keploy Date: May 2024 - Jan 2025 Summary: Built sample applications and a RAG-based chatbot, designed homepage sections, and led developer relations and community programs. [Keploy](https://keploy.io) is an open-source, developer-friendly API testing platform that auto-generates test cases and data mocks from real traffic. ## What I did - Built **multiple new sample applications** and improved existing ones using Rust, Flask, MongoDB, Spring Boot, and Django to demonstrate Keploy’s testing capabilities. - Implemented a fully-functional **RAG-based AI chatbot** for Keploy's documentation page, helping developers navigate the docs faster. - **Designed and developed** new sections on **Keploy's main homepage**, improving the overall layout and developer experience. - Planned and hosted **Keploy Rain of Code**, an open-source contribution programme where I guided 50+ newcomers through making their first contribution to Keploy’s repositories, which also helped Keploy hire fresh talents. - Wrote **25+ technical blogs** covering topics in Software Testing, API mocking, and AI-assisted development. - Managed **social media** handles of Keploy and increased engagement rates by **120%** and follower count by **30%**. ## Software Developer • Wizdom URL: https://www.swapnoneel.site/work/wizdom Date: Jan 2024 - May 2024 Summary: Revamped the app UI and website, optimized codebase performance to reduce load times, and enhanced the podcast audio playback experience. [Wizdom](https://wizdomapp.com) is a platform that provides book summaries and insights via podcasts and readables, helping users learn from the world's best books in minutes. ## What I did - **Revamped the old UI** and built a modern, intuitive interface for their app, significantly improving the overall user experience for over 20,000 users. - **Optimized the codebase** with a focus on performance and scalability, significantly reducing load times by **1200-1800ms** and ensuring a smoother experience across the platform. - **Expanded and rebuilt their website**, creating responsive and high-performance websites that align with their new branding. - **Enhanced the podcast experience**, implementing features that improved audio playback and user engagement with audio content. - **Coordinated with the product and design teams** to successfully transition from legacy systems to a modern tech stack. ## Technical Writer • Tutorials Point URL: https://www.swapnoneel.site/work/tutorials-point Date: May 2023 - May 2025 Summary: Authored a successful Advanced Python course, contributed to database and web courses, and edited over 200 technical articles as a content manager. [Tutorials Point](https://www.tutorialspoint.com) is one of the most popular platforms for learning programming and technology, with millions of learners worldwide. ## What I did - Created an **Advanced Python Course** with over **40 modules** that was purchased by **9,800+ users** over a period of 12 months, making it one of the most successful courses I've shipped. - Contributed to **MySQL** and **Web Development** courses which helped on-board new learners to the platform. - As a content manager, **edited over 200 articles** from 10+ technical writers who worked under me. - Maintained clarity, accuracy, and accessibility in all written content, keeping it approachable for beginners while technically sound for intermediate developers. --- # Projects ## Mesh Hop URL: https://www.swapnoneel.site/projects/mesh-hop Date: 2026-07-19 Summary: Desktop app that automatically discovers, verifies, and routes an isolated browser profile through the best public proxies. A lightweight, self-contained desktop application that automatically discovers working public proxies for a chosen region, verifies them end-to-end, and routes a dedicated browser profile through the best one. It automates the tedious parts of finding, testing, ranking, and rotating public exits, and wires a hardened Firefox profile to the result. ### Tech Stack - **Rust** — Tauri for the lightweight, secure cross-platform desktop framework and sidecar process execution - **Next.js** — for a responsive, modern desktop dashboard and user interface - **Node.js** — for the custom concurrent proxy discovery and benchmarking engine (bundled as a sidecar) - **Tailwind CSS** — for custom theme-driven user interface components - **Firefox** — for launching isolated, proxy-hardened browser profiles with uBlock Origin pre-configured ### Features - Automated **proxy discovery** pulls current candidates (HTTP, HTTPS, SOCKS4, SOCKS5) from multiple public providers. - End-to-end **proxy verification** tests candidates concurrently via HTTPS requests to Cloudflare's trace endpoint to verify country and IP. - Performance **profiling and benchmarking** measures steady-state download speed, speed consistency, and network classification. - One-click **proxied browsing** launches a dedicated, isolated browser profile with reduced WebRTC/DNS leakages and pre-installed extensions. - Dynamic **IP rotation** rotates proxies or refreshes candidates instantly from the desktop interface. ## Blame URL: https://www.swapnoneel.site/projects/blame Date: 2026-07-01 Summary: Client-side Next.js web application designed to scan GitHub repositories, resolve contributor identities, and audit commit counts directly in the browser. A powerful client-side contributor aggregation and outreach tool designed to answer one crucial question for any GitHub repository: "who actually contributed, and how much?" By extracting names, emails, and commit frequencies directly from the browser, Blame enables recruiters, project leads, and open-source audit tools to quickly locate and connect with repository contributors. ### Tech Stack - **Next.js** — powering the client-side single-page web application with Turbopack, React 19, and the App Router - **TypeScript** — ensuring static typing, safety, and robust code structures for component interfaces and utility functions - **GitHub REST API** — fetching commit lists, resolving user profiles, and aggregating histories directly from the browser without intermediate servers - **Tailwind CSS** — styling the user interface with a sleek, modern, dark-themed responsive layout and interactive animations ### Features - Runs client-side to query `api.github.com` without backend storage, keeping scans private - Employs a DP-based fuzzy matcher to filter results with word-boundary and run bonuses - Supports personal access tokens directly from the browser to raise API rate limits - Groups commits by GitHub profile and resolves usernames from private email aliases - Exports aggregated results as a CSV file or copies them directly as markdown - Filters merge commits, sorts results, and limits the total contributor list size ## ANRL URL: https://www.swapnoneel.site/projects/anrl Date: 2026-06-08 Summary: Graph-native representation language explicitly designed to optimize attention allocation and semantic saliency for Large Language Models. A paradigm-shifting representation system designed specifically for Large Language Models, replacing human-centric data formats like JSON with transformer-optimized schemas. By explicitly encoding how a model should "think" about data, ANRL eliminates attention drift and structural noise to provide AI systems with prioritized reasoning, epistemic clarity, and unshakeable relational anchoring. ### Tech Stack - **Rust Compiler** — for a seamless, high-performance pipeline that ingests standard formats into ANRL streams - **MessagePack** — for sub-token, highly dense binary serialization of complex graph structures - **Tree-Sitter** — for robust, syntax-aware code ingestion from multiple programming languages into ANRL schemas - **Fastembed** — for opt-in semantic enrichment and smart auto-sensing of implicit document relationships ### Features - Explicit **saliency weighting** to definitively direct the transformer's attention to critical information - Built-in **epistemic confidence markers** to dynamically encode truthiness and prevent hallucinations - Robust **relational anchoring** to securely link conceptual nodes and completely prevent column slippage - Native **causal logic operators** to explicitly distinguish causation from mere association during reasoning steps - High **token density syntax** specifically designed to minimize structural noise and delimiters for optimal context utilization ## Term Chat URL: https://www.swapnoneel.site/projects/term-chat Date: 2026-05-09 Summary: Terminal-based messaging hub for real-time communication, collaboration, and AI-powered interactions. A terminal-based messaging hub designed for real-time communication, group collaboration, and AI-powered interactions, all within the command-line interface. ### Tech Stack - **Node.js** — for the core CLI application (`termchat-cli`) - **npm** — for the core CLI application (`termchat-cli`) - **Supabase** — for the backend and database management - **Cloudflare R2** — for secure cloud storage and file transfers - **Google Gemini API** — for persistent AI-powered chat assistance - **bcrypt** — for secure CLI-based authentication ### Features - **Real-time messaging** for instant DMs and group chats with live presence and unread counters - **AI integration** powered by Google Gemini with persistent chat history and context management - Seamless **sharing of files and directories** (auto-zipped) via Cloudflare R2 - **Secure auth** with CLI-based registration and login with session persistence - **Social ecosystem** that includes global user search, activity-sorted friend lists, and friend request management ## Folio URL: https://www.swapnoneel.site/projects/folio Date: 2026-05-08 Summary: Premium portfolio engine designed for developers and designers. Folio is a state-of-the-art portfolio generation platform designed for developers, designers, and creatives who want a professional, high-performance web presence without the overhead of manual coding. ### Tech Stack - **TanStack Start** — for a seamless, type-safe development experience and ultra-fast performance - **TypeScript** — powering the core application logic and modern state management - **React** — powering the core application logic and modern state management - **Tailwind CSS** — providing a modern, responsive, and highly customizable UI system - **Supabase** — for robust authentication and real-time database persistence - **Radix UI** — for accessible and high-quality UI primitives - **@dnd-kit** — for smooth and intuitive drag-and-drop section management ### Features - Choose from **premium themes** including Terminal, Vercel, Material You, and Editorial - **Live preview dashboard** to see changes in real-time as you customize your content - **Custom section templates** for Galleries, Stats, Timelines, and Link Cards - **Granular customization** with per-section accent colors and theme overrides - **Built-in image support** for avatar and gallery uploads with seamless Supabase integration ## Scholarian URL: https://www.swapnoneel.site/projects/scholarian Date: 2026-05-07 Summary: High-end research platform designed to transform academic tools into a focused, analytical journey. A high-end, editorial research platform that replaces manual literature reviews with an intelligent pipeline that scours semantic databases and engages in context-aware interrogation. ### Tech Stack - **Next.js** — for a premium, type-safe analytical lens workspace - **TypeScript** — for a premium, type-safe analytical lens workspace - **Google Gemini AI** — powering query enrichment and automated synthesis - **Supabase** — for database persistence and secure authentication - **Zustand** — for global state management and optimistic UI updates - **Tailwind CSS** — for a custom-curated, responsive design system - **Base UI** — for high-quality, accessible UI components - **Radix** — for high-quality, accessible UI components ### Features - **Persistent research history** for every search run and report is persisted via Supabase - **Stateful research pipeline** for real-time tracking from query enrichment to report generation - **Smart mode** that prioritizes the finding of the absolute best papers through repeated searches - **Smart refinement** is built-in for intent analysis to narrow down research topics - **Analytical lens workspace** for a premium responsive dashboard optimized for all devices - **PDF export engine** that generates high-fidelity PDF versions of research reports ## Get Response URL: https://www.swapnoneel.site/projects/get-response Date: 2026-05-06 Summary: Terminal-based AI chatbot and automation tool. A terminal-based AI chatbot and automation tool that turns your command line into an intelligent assistant. ### Tech Stack - **Node.js** — for the core CLI logic - **Google Gemini API** — powering the AI-powered responses - **Stack Exchange API** — powering the specialized research mode for technical querying - **Tesseract.js** — for extracting context from images and documents - **PDF-Parse** — for extracting context from images and documents - **Mermaid.js** — for automatic codebase visualization and diagram generation - **Boxen** — for a polished, interactive, and user-friendly terminal interface - **Chalk** — for a polished, interactive, and user-friendly terminal interface ### Features - Ask questions from **direct text input**, file content, or entire directory context - Enter a **persistent chat mode** for back-and-forth conversational sessions - **Automate terminal commands** by describing tasks in plain English - Generate content and handle technical tasks like writing unit tests or scaffolding applications ## Toile URL: https://www.swapnoneel.site/projects/toile Date: 2026-05-05 Summary: Minimal CRUD application to showcase artistic works in an elegant way. A clean, easy-to-use CRUD application for showcasing artistic works — built with a minimalistic and elegant design philosophy. ### Tech Stack - **React.js** — component-driven UI with smooth interactions - **GSAP** — animations and page transitions - **Tailwind CSS** — utility-first styling for a responsive layout - **Appwrite** — backend for CRUD operations and seamless user authentication ### Features - Create, read, update, and delete artistic entries - Smooth, performant animations powered by GSAP - Secure user authentication via Appwrite - Fully responsive across mobile and desktop ## Omni Learner URL: https://www.swapnoneel.site/projects/omni-learner Date: 2026-05-02 Summary: Universal AI-powered translation platform design for educational accessibility. An award-winning design concept developed for the Smart India Hackathon 2023, aimed at breaking language barriers in education through high-speed, affordable AI translations of global resources. ### Tech Stack - **Figma** — for end-to-end UI design, from low-fidelity wireframes to high-fidelity interactive prototypes ### Features - **256+ language support**, a massive scaling concept designed to make global knowledge truly accessible - **Lightning-fast translation** where the UI workflow is optimized for translating educational resources in seconds using deep learning - Designed with a focus on student accessibility, featuring **low-cost tiers** for individual users - An intuitive **"Explore" functionality** to discover translated resources from language A to language B ## Y Dub URL: https://www.swapnoneel.site/projects/ydub Date: 2026-05-01 Summary: Premium AI-powered video dubbing platform design. A high-fidelity design conceptualized for the Smart India Hackathon 2023, focused on making AI video dubbing accessible, affordable, and accurate for the Indian market. ### Tech Stack - **Figma** — used for end-to-end UI/UX design, from wireframing to interactive prototyping ### Features - **Lip-sync conceptualization** with an intuitive interface for a deep-learning-based lip-syncing engine - **Inclusive language strategy** designed to accommodate multi-script layouts for 32+ Indian languages - **Growth-driven UI** that incorporated high-impact data visualization (10x watch time, 300% revenue) to bridge user trust --- # Blog Posts ## AI Observability Explained: What It Is and How It Works URL: https://www.swapnoneel.site/blog/ai-observability-explained Date: 2026-08-18T06:25:25.000Z Summary: AI observability is how you see inside a non-deterministic system. What to trace on every call, why logs are not enough, and where it belongs. Traditional monitoring rests on one quiet assumption that nobody ever writes down: the same input gives you the same output. Something breaks, you replay the request, you watch it break again, you fix it. Now send the same request to a model twice. You get two different answers, and neither one of them threw an error. **AI observability** is the practice of recording what happened inside an AI system on every request: the prompt, the model version, tokens, cost, latency, tool calls, and a judgement of whether the output was any good. Monitoring tells you the service is up. Observability tells you why it answered that way. That gap is the whole story here. ## Why your current monitoring stack misses all of this Your existing setup is watching for crashes. Status codes, error rates, p99 latency, memory. All of it is designed around the idea that a broken thing looks broken. An AI feature failing looks nothing like that. It returns HTTP 200 in 900ms, with grammatically perfect prose that happens to be wrong, or that quietly ignored the document you retrieved for it, or that called the refund tool when the user only asked a question. Your dashboard sees a healthy service, because by every measure it has, the service is healthy. And there are whole categories of failure your stack has no field for. It has nowhere to put "this response cost 14 cents", or "the model version changed under us last Tuesday", or "the retrieved context was garbage". Those are not infrastructure facts, and standard telemetry was never built to carry them. ![Why standard monitoring misses AI quality failures](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/72i1wisqwyz7zwvurepu.png) Something has to hold those fields instead, which is the entire reason this tooling exists. My team uses [Bifrost](https://www.getmaxim.ai/bifrost), so I will use it as the example throughout this post. It's an [open-source AI gateway](https://github.com/maximhq/bifrost) from Maxim, so anything I claim about what it records per request is something you can go check line by line. Most tools here put their telemetry story on a marketing page and stop there. ## What one AI request actually looks like when you trace it This is the part that made it click for me, so let me walk through a real shape. At [Keploy](https://keploy.io) I built a retrieval-augmented chatbot over their documentation, using vector embeddings, so developers could ask a question instead of hunting through pages. A single question to something like that is not one operation. It is a chain, and a trace is just that chain written down. One request breaks into spans, where a **span** is one step with its own start time, end time, inputs and outputs: 1. The user's question comes in and opens the root span. 2. The question gets embedded into a vector. That is a span, with its own model and its own cost. 3. The vector search runs and returns, say, five chunks of documentation. That is a span, and the important bit is that it records _which_ five chunks came back. 4. Those chunks get stuffed into a prompt template along with the chat history. 5. The model call goes out. This span carries the model name and version, the temperature, the prompt tokens, the completion tokens, the cost in dollars, the total latency, and the time to first token. 6. If the model calls a tool, every one of those is its own child span too. Now here is why anybody bothers with all that plumbing. When the bot gives a bad answer, you do not have to guess. You open the trace and look at step 3. If the vector search pulled back five irrelevant chunks, your problem is chunking or embeddings, and the model did nothing wrong. If the search pulled back exactly the right documentation and the model still answered from thin air, your problem is the prompt. Two completely different fixes, and without the trace you cannot tell them apart. All you have is "the bot said something dumb", which is the single most useless bug report in the world. ## The things worth capturing on every call You will notice I have not called this section "the three pillars of observability". Everyone else writing about this does, and I dropped it on purpose, because logs, metrics and traces is a framing built for deterministic systems and it has no slot at all for "was the answer any good". ![The telemetry worth capturing on every AI call](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/3s0utdi83ubyijnvze1x.png) So here is the actual list: - **The full prompt and the response**, as they really went over the wire, after every template and system message got assembled. Not the template, the final text. - **Model, version and parameters.** Providers ship silent updates. If you cannot say which exact version answered a request, you cannot explain last month's regression. - **Tokens in, tokens out, and cost in dollars** per request, attributed to a user or a feature. - **Latency, split into total time and time to first token.** Those two numbers feel completely different to a user, and one can get worse while the other improves. - **Tool calls, retries and fallbacks.** Which key was tried, what failed, what it fell back to. - **A trace ID that ties the whole chain together**, and ideally a session or user identifier so you can reconstruct a full conversation. - **A quality score**, attached after the fact. More on that next. I built one of these myself earlier this year, an internal tool at a contract role that captured an AI product's logs and turned them into reports on latency and probable slowdowns (keeping it vague on purpose, cannot say much more than that). The honest takeaway was not that the tool was clever. It was that a team can ship for months on vibes, and the moment somebody puts the per-request numbers on a screen, problems nobody previously had words for suddenly have words. ## How do you measure quality when there is no right answer? Well, you do not measure it the way you measure a unit test, because there is no expected string to compare against. The industry has mostly settled on three overlapping things. **LLM-as-judge**, where you send the input and output to a second model with a rubric and it scores relevance or faithfulness or tone. It is imperfect, and it is far better than nothing. **Human annotation** on a sample, which is slow, expensive, and still the ground truth everything else gets calibrated against. And **implicit user signals**, like thumbs, edits and retries, which are noisy but free. Run those continuously and you get **drift detection**, which is just the same score measured over time. When your faithfulness score drops 8 points over two weeks and nobody deployed anything, something moved underneath you, and that is usually the model provider. ![Three ways to measure AI output quality](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/rhkiusbk7ev7rfgmtj6c.png) This is the half most teams skip, and there are numbers on it. In LangChain's [2026 State of Agent Engineering report](https://www.langchain.com/state-of-agent-engineering), which surveyed 1,300+ practitioners, 89% said they had observability running on their agents while only 52% were running evaluations. So most people are recording what happened and still have no systematic opinion on whether it was good. Which is also why I keep saying you cannot test an AI feature the way you test code. I went into that failure mode properly in my post on [testing AI coding agents](https://www.swapnoneel.site/blog/testing-ai-coding-agents). ## Where AI observability actually lives in your stack Two choices here, and you can do both. You can instrument your application directly, wrapping every model call in your own code. That gives you the most context, because your code knows what the user was doing. It also means every service, every language and every framework has to be instrumented separately, and someone has to keep it consistent. Or you put it in the gateway. If all your model traffic already goes through one proxy, that proxy sees every request and every response by definition, and you get telemetry for services you never touched. And the reason you can do both without doubling the work is that there is finally a shared standard. The [OpenTelemetry GenAI semantic conventions](https://opentelemetry.io/blog/2026/genai-observability/) define agreed attribute names for exactly this, like `gen_ai.request.model`, `gen_ai.usage.prompt_tokens` and `gen_ai.usage.cost`. Emit those and your AI spans slot into the same traces as the rest of your system, in whatever backend you already pay for. Bifrost is a reasonable thing to look at here, since it does both halves. It records inputs, outputs, tokens, cost and status for every call into SQLite or Postgres with a dashboard on top, and it exports OpenTelemetry spans using those GenAI conventions plus native Prometheus counters like `bifrost_input_tokens_total` and `bifrost_cost_total`. The logging runs in background goroutines, which is why [its documentation](https://docs.getbifrost.ai/features/observability) puts the added overhead under 0.1ms per request. That last detail is the pattern to steal, whichever tool you end up picking. Telemetry gets emitted off the hot path, after the response is already on its way back to the user. Observability that slows down the thing it observes gets switched off within a week. The routing side of that same gateway is worth knowing about too, and I covered it in my post on [adaptive load balancing](https://www.swapnoneel.site/blog/what-is-adaptive-load-balancing). ## What it costs you to run Now the uncomfortable part, because none of this is free. ![The storage, privacy, and attention costs of observability](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/78xs5rtn8pt5huvu3cyl.png) **Storage adds up fast.** You are storing full prompts and full responses, and prompts got long. A retrieval app can easily push 8,000 tokens of context per call. At real traffic that is a serious volume of text, and this is where sampling comes in: keep 100% of errors and slow requests, keep a small percentage of the healthy ones. **Your prompts contain user data.** Every support chat, every uploaded document, every email a user pasted in. The moment you log all of it, your observability store is now a system holding personal data, with all the retention and access rules that implies. Redact at the point of capture, not later. **And someone has to actually look at it.** This is the one that quietly kills the whole effort. The traces get collected, the dashboard gets built, nobody opens it, and six months later it is a very expensive write-only database. ## Frequently asked questions **Is AI observability the same as LLM monitoring?** Close, and monitoring is the narrower one. Monitoring tracks known metrics like uptime, latency and error rate, and answers "is it working". Observability keeps enough per-request detail that you can answer questions you had not thought of yet, like "why did this one user get that answer". In practice most tools sell both under one name. **Do I need OpenTelemetry for this?** No, but it is the sensible default in 2026. The GenAI semantic conventions mean your AI spans use the same attribute names everywhere, so you can change vendors without reinstrumenting, and your model calls appear inside the same traces as your database queries. Note that parts of the spec are still marked experimental, so pin your versions. **What is the difference between observability and evals?** Evals are the measurement, observability is the pipe. Evals score whether an output was good; observability captures the request, the context, the cost and the trace so the score has something to attach to. You can run evals offline in CI against a fixed dataset, but you can only run them on real traffic if the traffic is being recorded. ## So what should you actually do? If you have an AI feature in production right now and you cannot pull up the exact prompt, the model version and the cost of a request from last Tuesday, that is the gap, and it is worth a day of your week. Start with capture, and not with dashboards. Get every request logged with its prompt, response, model, tokens, cost and a trace ID, put it wherever you already look at data, and give it two weeks. You will find something. Everyone does. Quality scoring, drift alerts and per-feature cost budgets are worth adding later, but every one of them sits on top of the boring capture layer, so there is no point doing them first. If your model calls already go through a gateway, turn on the telemetry it ships with before you write any of this yourself. That is the single highest-value hour available to you here, and it is mostly a config change. ![Capture first, then add quality, drift, and cost controls](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/0jqt2fv5869zcc74abon.png) That is my read on it, and your setup might look nothing like mine. If you have built this kind of tracing yourself, or you have had an AI observability bill genuinely surprise you, drop it in the comments, I would like to hear how it went. You can find me on [X](https://x.com/swapnoneel123) where I post about most of what I am building, and the rest of my writing lives at [swapnoneel.site](https://www.swapnoneel.site). ## Bifrost for Enterprises: Adaptive Routing, Guardrails and much more URL: https://www.swapnoneel.site/blog/bifrost-for-enterprises Date: 2026-08-09T15:41:05.000Z Summary: I explored the enterprise features of Bifrost, including Audit Logs, MCP Tool Groups, Adaptive Routing, and custom Guardrails, and provided an honest verdict. As you all might have seen, in the past two blogs, I wrote about how I explored the different features of Bifrost, and how each one of them improved my workflow, and how I interact with different harnesses through one common gateway. If you haven’t read them yet, go check them out from [the blog archive](https://www.swapnoneel.site/blog). So after exploring all the free features, I was getting the urge to try the Enterprise version as well (for my personal use, though). So, I contacted the [Bifrost](https://github.com/maximhq/bifrost/) team, and thanks to them, they gave me limited access to try out their paid features for free!! So, in this blog, I will be exploring the most prominent paid features, and would give an honest verdict on whether it’s great for personal use or not, or whether you should even give it a try for your enterprise use case. ## Transparency through Audit Logs So, before creating any new Enterprise configuration, I wanted to see whether Bifrost could actually tell me what was happening behind the scenes. Audit Logs tell you exactly that. It’s different from the regular LLM logs because they show the requests going through the gateway, while Audit Logs focus on changes and administrative activity inside Bifrost. So if someone creates a virtual key, changes a routing rule, updates a guardrail, or modifies the cluster configuration, this is where we should be able to find it. ![Bifrost dashboard showing the Audit Logs interface.](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/cgnv1ejfcvkgmrohejyj.png) It is not the most exciting feature on its own, and for personal use, this might not be that useful, but in an enterprise setting, it is probably one of the most important ones to have, when you are working with a large group of people, and you have to keep tabs on everything that’s going on. ## What are MCP Tool Groups? In my [previous blog](https://www.swapnoneel.site/blog/deep-dive-into-bifrost), I already mentioned how I connected the MCP Gateway to OpenCode using the Virtual Key, which enabled Bifrost to expose all of my configured MCP tools through that one endpoint. But that also raised a question: do I really want every harness to have access to every tool? So, this time I decided to try MCP Tool Groups. The idea is pretty simple. We can create a group of selected MCP tools and attach that group to a virtual key. OpenCode already uses my dedicated Enterprise virtual key, so I can control the tools available to it without changing the rest of my MCP setup. In the previous blog, you might have seen that I’ve used the Context7 MCP server. It provided access to two tools: - `resolve-library-id` - `query-docs` So, while creating the tool group, I decided to drop the `query-docs` and kept only the `resolve-library-id` activated. ![Bifrost dashboard showing the 'Edit Tool Group' panel](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/re84bfyifr07hwij3hl5.png) Then, under Associations, I attached the group only to my opencode-enterprise virtual key. I didn't attach it to any teams, customers, providers, or other keys. The OpenCode MCP configuration itself didn't need much change. It was already pointing to Bifrost's remote MCP endpoint: ```json { "mcp": { "bifrost": { "type": "remote", "url": "https://bifrost-enterprise.agitracker.io/mcp", "enabled": true, "oauth": false, "headers": { "Authorization": "Bearer {file:./bifrost-virtual-key}" } } } } ``` I kept the virtual key inside a separate local file, so it never had to be pasted into the configuration or committed to Git. After restarting OpenCode, I used a deliberately small prompt, `Use the Context7 MCP to resolve the React library. Reply with only the returned library ID.` ![Bifrost MCP logs dashboard showing metrics](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/fncld2oo6svgypq11g2t.png) The request executed successfully, as you can see in the above screenshot. So, is this feature useful? The idea is excellent, especially when different coding harnesses should have access to different MCP tools. And, it gives you a central place to manage tool access instead of duplicating MCP configuration across every client. ## What is Adaptive routing? The next feature I wanted to try was Adaptive Routing. The idea behind it is quite useful, especially if you are running several models, providers, or API keys through the same Bifrost gateway. Normally, requests are distributed using fixed weights. For example, if two Gemini keys have the same weight, Bifrost can send roughly half of the traffic to each one. The problem is that fixed weights do not know whether one key has become slower, started returning errors, or hit a rate limit. Adaptive Routing tries to solve that automatically. Bifrost monitors the latency, error rate, success rate, and utilization of each available route. It then recalculates their weights every few seconds. And, a healthy and faster route receives more traffic, while a failing or slow route receives less. Bifrost still sends a small amount of traffic to recovering routes so it can detect when they become healthy again. The routing happens at two levels: 1. Bifrost can select which provider should handle a model request. 2. After selecting the provider, it can choose the best API key configured for that provider. This makes the feature more useful for companies that maintain multiple provider accounts or keys. Instead of manually changing weights whenever a provider starts acting up, Bifrost can react to the recent performance data on its own. ## How to set Guardrails? Every model has a set of their own guardrails by default, but while working on them you might need to put your own custom guardrails as well. And the best place to do that is to integrate it directly into your AI gateway! This feature is meant to protect both the prompts sent to a model and the responses coming back from it. Bifrost separates the feature into two parts: rules decide when a check should run, while profiles define what kind of check should be performed. For this test, I wanted to avoid adding another external API key, so I chose Bifrost's built-in Custom Regex provider. According to the [Guardrails documentation](https://docs.getbifrost.ai/enterprise/guardrails), Custom Regex runs locally and can be used for deterministic pattern checks. So, I created two guardrail rules. One for the input, and the other one for the output. ![Bifrost dashboard showing the 'Edit Guardrail Rule' sidebar interface](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/5hko4nft3m5do9q2uxxm.png) So, now if I send a request like `Reply with exactly: BIFROST_GUARDRAIL_TEST`, I get a `regex pattern matched` error. That’s where the guardrail is actually doing its job. It is as simple as that. No fancy setup needed for a working guardrail. ## Final thoughts When I started exploring Bifrost Enterprise, I expected the paid version to feel like the open-source gateway with a few extra switches. That wasn’t true. The core experience stayed familiar, and I could continue using OpenCode through one Bifrost endpoint while the gateway handled the provider connection underneath. That part was convenient. I did not need to change my workflow every time I switched between OpenAI and Gemini. The enterprise features that made the most sense to me were Audit Logs and MCP Tool Groups. Audit Logs give teams a central record of what happened, while MCP Tool Groups make it easier to control which tools a client can access. Would I use Bifrost Enterprise for my personal setup? Probably not if I only had one provider, one API key, and a handful of requests. The extra governance and operational features would be more machinery than I need. For a team running several models, provider keys, MCP clients, and internal users, the situation is different. A shared gateway, centralized logs, access controls, guardrails, and tool restrictions can remove a lot of repeated setup from individual applications. And if you think you are the right candidate, you can always [book a demo](https://www.getmaxim.ai/bifrost/book-a-demo)! ![Please like, follow and share!](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/8caibv8q641pm9okuo0d.png) ## Deep Diving Into Bifrost: Virtual Keys, MCP and Skills URL: https://www.swapnoneel.site/blog/deep-dive-into-bifrost Date: 2026-07-31T00:00:00.000Z Summary: Going further into the Bifrost dashboard: virtual keys and rate limits, the MCP gateway, prompt and skill repositories, and custom log headers. In my previous blog, I mentioned how I got tired of switching providers every time I hit a rate limit, and how I finally found Bifrost, which actually solves that. If you haven't read it, [please do check it out from here!](https://www.swapnoneel.site/blog/trying-bifrost-ai-gateway) Since then, I have been daily driving [Bifrost](https://github.com/maximhq/bifrost/) with multiple coding harnesses including OpenCode, jcode and Pi, to name a few. Initially, I was perfectly fine with just the fallback mechanism and complexity routing. But every time I skimmed through the dashboard, the other features kept intriguing me. So, I sat down and decided to explore all of them one-by-one, and see if any of them solve problems that I'm not even aware of yet. So, let's begin! ## Setting Limits With Virtual Keys So, you can create a virtual API key and set custom limits on how many tokens and requests it can allow in a set time period. Setting it up is pretty easy through the dashboard. ![The Bifrost virtual keys dashboard, creating a key named opencode-local with Gemini and OpenCode Zen attached to it](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/a9fhtat7d34dmp2h8mov.png) Just give it a name and an expiry duration, then add the providers and the models you want to allow through this key. And done! You can take this key and set it up in your desired harness, similar to the way we discussed [in the earlier blog](https://www.swapnoneel.site/blog/trying-bifrost-ai-gateway). But this time, we should make a separate file for the key, and keep it in the same directory as our opencode config file. I've named mine `bifrost-virtual-key`, and it holds nothing but the key itself. OpenCode can read a value straight out of a file using the `{file:...}` syntax, and a relative path there resolves against the config file's own directory, not wherever you happen to launch the terminal from. So the key never has to sit inside the config, and never has to go into git. After that, pasting this in our config: ```json { "model": "bifrost-local/gemini/gemini-2.5-flash", "small_model": "bifrost-local/gemini/gemini-2.5-flash", "provider": { "bifrost-local": { "npm": "@ai-sdk/openai-compatible", "name": "Bifrost Local", "options": { "baseURL": "http://localhost:8080/v1", "apiKey": "{file:./bifrost-virtual-key}" }, "models": { "gemini/gemini-2.5-flash": { "name": "Gemini 2.5 Flash via Bifrost" }, "opencode-zen/big-pickle": { "name": "Big Pickle via Bifrost" } } } } } ``` The `small_model` field there is worth a word, since it's easy to skip past. OpenCode uses it for cheap background work like generating session titles, and if you don't set it, it goes hunting for a cheaper model on its own. Pointing it at the same model keeps everything flowing through one virtual key, which is the whole point of the exercise. This feature is pretty useful when you are experimenting with different models, and for long-horizon tasks, where you don't want the model to run indefinitely and get stuck in a loop, just burning expensive tokens. Or whenever you want fine control over the MCPs, tools or skills that your harness can get access to. So let's set a deliberately tiny limit and watch it bite. ![Rate limiting configuration for the virtual key, set to a maximum of 1000 tokens and 10 requests, both resetting hourly](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/n1uzzssp5q6qmi5v1vhn.png) As you can see, I've set a maximum of 1000 tokens and a maximum of 10 requests, both resetting every hour. That token budget is small on purpose, because I want to hit the wall quickly rather than wait around for it. I've already connected this key to my OpenCode setup, so a couple of ordinary messages should be enough to get me blocked. ![Bifrost logs showing two successful requests that burned 13.57K tokens, followed by ten failed requests once the token limit was crossed](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/6t4h091k4dsmanep2cfy.png) And that's exactly what happened, though the way it happened is the interesting part. My first message cost 627 tokens and went through fine. The second one went through too, and quietly cost 12.94K tokens by itself. Every single request after that got rejected, which is why the success rate sits at 16.67%, only 2 requests out of 12. So the limit isn't checked against the size of the request you're about to make, it's checked against what you have already spent. A request gets waved through as long as you're under budget at that moment, and it can then blow straight past the ceiling on its own. Worth knowing before you set a budget you actually care about, because the cap decides when the blocking starts, and not how much you can spend in total. One more thing to notice in that screenshot: each failed attempt shows up twice, once against gemini and once against big-pickle. That's the fallback rule from the last blog doing its job. Bifrost tried the fallback, and the fallback got refused by the same virtual key, which is exactly what you'd want. Now, let's see what more we have in our box! ## The MCP Gateway This is the one that I regret not trying earlier. Using the MCP gateway we can connect all our MCPs in one place, and then selectively allow access to our harnesses using the virtual key. So instead of every harness carrying its own copy of every MCP config, Bifrost holds them all, and each key gets to see only the slice you've allowed it. There are a huge number of MCPs already present in the [MCP Server library](https://www.getmaxim.ai/bifrost/mcp-servers), 487 of them at the time I'm writing this, and it's just a one-click installation from there. As you can see in the screenshot below, I have already installed the Context7 MCP. And now, I will just add this to my virtual key from the MCP Client Configuration option, and we are good to go. ![The Bifrost MCP Server Library showing 487 available servers, with Context7 marked as installed](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/52yz7mhdo4guxv5lcs42.png) As we have already added our virtual key to our OpenCode harness, we can now easily use the Context7 MCP from there. Bifrost exposes every MCP you've installed at a single endpoint, `/mcp`, and the virtual key you send as a Bearer token decides which tools come back. So from OpenCode's side, this looks like one ordinary remote MCP server, no matter how many you have installed behind it. Here's the block to add to the config file: ```json { "mcp": { "bifrost": { "type": "remote", "url": "http://localhost:8080/mcp", "enabled": true, "oauth": false, "headers": { "Authorization": "Bearer {file:./bifrost-virtual-key}" } } } } ``` The `oauth: false` line matters more than it looks. OpenCode will try to start an OAuth flow on its own when a remote MCP server answers with a 401, so you have to tell it not to bother here, because we are authenticating with a fixed key instead. Let's test it by asking OpenCode something that can only be answered by fetching live documentation, and then check whether the tool calls actually show up on the Bifrost side. ![Bifrost MCP logs showing two successful Context7 tool executions, resolve-library-id and query-docs](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/z86aq2lb1z6jcc9xygxu.png) And there they are. Two tool calls, `resolve-library-id` followed by `query-docs`, both against the Context7 server, both successful. So the MCP is working perfectly as intended, and I never had to put a Context7 config into OpenCode at all. Now, Bifrost has a similar thing going on for prompts and skills as well, so let's check them out too! ## The Prompt and Skills Repositories So, before coming to the skills, let's talk about the Prompt Repository. Here, we can test our prompts against various models, see the results and tweak them. This is really important, because I was able to thoroughly test my prompts here before turning them into a skill. And as you can see, I've created a code review prompt that accepts the language, the review focus and the code snippet, and gives you a detailed review of it, along with the revised code if your code needs any fixing. ![The Bifrost prompt playground running a code review prompt, with code, language and review_focus variables filled in on the right](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/6yl65270vb753sc5r70d.png) The variables are the neat bit here. Anything you write as `{{ code }}` or `{{ language }}` inside the prompt gets picked up automatically and turned into a field you can fill in, so you're changing the inputs and not rewriting the prompt every time you want to try something. And prompts here are versioned, which I didn't expect. You keep editing in a session, and when something is actually good you commit it as a version, so the thing your application calls later is a version you deliberately shipped, and not whatever you happened to be typing five minutes ago. Now once you are done testing your prompts, you can create a skill out of that as well, and add it in the skills repository. I've created this skill, `safe-bug-fix`, that can be used to fix bugs in a codebase. It's a plain SKILL.md, with a "when to use" section, a numbered workflow and a set of safety rules, and it's targeted at OpenCode. ![The safe-bug-fix skill in the Bifrost skills repository, showing its SKILL.md body with When to use, Workflow and Safety sections](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/x4gq8350s5mk3wqle4iz.png) After creating the skill, we need to configure OpenCode as well, so that it can actually use the skills. For that we can paste the following code snippet in our OpenCode config: ```json { "permission": { "skill": { "*": "allow" } } } ``` The `*` there means every skill is allowed. You can be pickier if you want, since the same block takes `deny` and `ask` alongside `allow`, and the keys accept wildcards, so something like `internal-*` can be denied while everything else stays open. And you will be able to use it inside OpenCode anytime you want! Now, the next most useful feature that I found is setting up custom log headers. Let's talk about that then! ## Custom Log Headers So, while I was primarily using my key on OpenCode, I started to use it on jcode as well. But I was unable to identify from the logs which request was coming from which harness. Hence I decided to find a solution, and found that we can create custom log headers in Bifrost from the Log settings. It was pretty simple to set up. The idea is straightforward: you name a header, and from then on Bifrost copies that header off every incoming request and stores it in the log entry's metadata. I added the header name `X-Request-Source`. After that, I configured OpenCode to actually send that header, by pasting this in the config file: ```json { "provider": { "bifrost-local": { "npm": "@ai-sdk/openai-compatible", "name": "Bifrost Local", "options": { "baseURL": "http://localhost:8080/v1", "apiKey": "{file:./bifrost-virtual-key}", "headers": { "X-Request-Source": "opencode" } } } } } ``` And, that's it! ![The Bifrost logs table with an extra X-request-source column, showing opencode against every request](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/4loyitgxbfeatne9i0tw.png) As you can see in the screenshot above, there's now an extra column in the logs, and I can tell that these requests are coming from OpenCode. Repeat the same block in your other harness with a different value, and the whole picture separates out. There's also a shortcut I found afterwards, which I'd have used if I had known about it. Any header you send with an `x-bf-lh-` prefix gets captured into the log metadata automatically, without configuring anything on the Bifrost side at all. The prefix gets stripped and whatever is left becomes the key. So `x-bf-lh-source: opencode` would have got me the same result with one less step. It was as simple as that! ## So, What's Actually Worth Using? Honestly, more of it than I expected, and a bit annoyingly so. Every one of these had been sitting in that sidebar the whole time I was happily using Bifrost as a fallback router and nothing else. The MCP gateway is the one I'd tell you to try first. Moving every MCP out of individual harness configs and into one place, then handing each harness a key that decides what it can see, fixed a mess I had stopped noticing because I'd been living in it for so long. Virtual keys are the ones I'd keep the tightest grip on, now that I know the limit gets checked against what you have already spent and not against what you're about to spend. And custom log headers took about two minutes and solved something I'd been squinting past for weeks. The prompt and skills repositories I'm still making my mind up about. Testing a prompt properly before shipping it is genuinely useful, and the versioning is better than what I was doing before, which was nothing. But I've not used them long enough to tell you whether they replace the way you already keep your prompts, or just sit beside it. So if you're running Bifrost as a gateway and never got past routing, go open that sidebar. There's more in there than I expected, and all of it stays local. And if you want to get a taste of the enterprise version, then you can easily [book a demo](https://www.getmaxim.ai/bifrost/book-a-demo). ![Please like, share and follow](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/p08hea4cd4ubq3zystk1.png) And if you try any of these, or you've found something in Bifrost that I've still not touched, tell me about it, or come find me on [X](https://x.com/swapnoneel123). ## GEO for Developers: Get Cited by ChatGPT and Perplexity URL: https://www.swapnoneel.site/blog/geo-for-developers Date: 2026-07-08T00:00:00.000Z Summary: GEO for developers, minus the agency fluff. Here's what actually matters if you want ChatGPT and Perplexity to cite your blog posts in 2026. Have you ever asked ChatGPT or Perplexity a coding question and get a suspiciously specific, correct answer, with zero link back to whoever actually wrote it? Yeah, that's been happening to my blog too, and I finally sat down to fix it. So here's the real answer: GEO for developers doesn't need an agency or a 40-page audit. It just needs three simple things: a properly structured content so a model can lift one paragraph and have it make sense on its own, real evidence instead of vague claims, and let the right bots get into your `robots.txt`. That's genuinely most of it, and in this blog I'll walk you through exactly how! ![AI search engines extracting content without source attribution](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/a877ztvlh1in45i8vcxu.png) ## What is GEO, actually? GEO stands for Generative Engine Optimization, and it's the practice of writing content so AI answer engines (ChatGPT, Perplexity, Google's AI Overviews) quote it directly instead of just ranking it in ten blue links. It's pretty self-explanatory, and it sits right next to SEO (Search Engine Optimization). We used to optimize purely for search engines like Google, but the times have changed, so now you're writing for generative engines too, Perplexity, Gemini, ChatGPT, all of them. The term comes from an actual peer-reviewed study, presented at KDD 2024 by researchers from Princeton, Georgia Tech, and IIT Delhi, and it's become its own line item in 2026 marketing budgets. [The paper's numbers are wild](https://www.omnibound.ai/blog/generative-engine-optimization-statistics): adding statistics to a page boosted its visibility in AI answers by 41%, and content optimized for generative engines improved visibility by up to 40% overall. ## Why doesn't most GEO advice fit a dev blogger? Here's the thing though, and I say this as someone who's done SEO freelancing and used Semrush since my first year of college: almost every GEO guide I found while researching this is written for a marketing team running brand-mention trackers across a hundred pages. That's not you if you're publishing one post a week on your own domain, or worse, on Hashnode. You don't need consensus-signal dashboards. You need to know which five things to do to your next post, and that's what I'm giving you. ## What actually works in GEO? **Answer the question in your first 40-60 words, standalone.** [Roughly 44% of everything AI engines quote comes from the first third of a page](https://www.omnibound.ai/blog/generative-engine-optimization-statistics), so don't bury your point under three paragraphs of throat-clearing. Say the thing, then explain it. If you notice carefully, I have already mentioned the three GEO optimisation steps in the introduction itself. **Phrase your headings as questions.** Not "Benefits of X", but "Why does X matter?" or "What is X?". This is also just a more natural way to write, so it's a rare case where the AI-friendly move and the human-friendly move are the same move. Check how I have framed the headings of each section =] **Every section has to make sense if someone rips it out of the page.** AI engines lift paragraphs, not entire posts. If your section starts with "This also means...", restate what "this" is. Small habit, big difference. **Put real numbers in, with sources.** Not "many developers prefer X", but "X handles 50,000 requests per second, per their own benchmark, published in June 2026". Vague claims don't get quoted. Specific, sourced ones do. You will find multiple such statements in this blog itself, and also with linked citations. **Fix your `robots.txt`.** This one's just a config file, and most bloggers never touch it (yes, that's a real file sitting on your domain right now, doing nothing). [The bots you want to allow for citations are different from the bots that scrape for training data](https://www.mersel.ai/blog/how-to-block-or-allow-ai-bots-on-your-website): `OAI-SearchBot` and `PerplexityBot` are the ones fetching pages to answer live questions, while `GPTBot` and `ClaudeBot` are the training crawlers. You can allow the first pair and still block the second, if that's the line you want to draw. ## Should you bother with llms.txt? Honestly? Probably not yet, and I want to be straight with you about this because most GEO posts won't be. The idea is simple: drop a markdown file at `/llms.txt` summarizing your site so a model doesn't have to parse your HTML. [Over 844,000 sites have added one already](https://www.mintlify.com/blog/what-is-llms-txt), including Anthropic's own docs. But no major AI company has confirmed they actually read it, and Google's John Mueller called it a "temporary crutch" that isn't done for search at all. So add one if you want, it costs you ten minutes and can't hurt. Just don't mistake it for the thing that's going to get you cited. That's the structure and the sourcing, not the file. ![Comparing lightweight llms.txt against structured site evidence](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/gvsazrctmc8kc6t6lrub.png) ## What if you don't even own your blog? If you cross-post to Hashnode or Dev.to like I do, you don't control the `robots.txt` on that domain, and you can't add schema markup either. That's the platform's call, not yours. What you can still control everywhere: the content structure itself, and your canonical URL. Always point the canonical tag back to your personal site, always write the answer-first paragraphs regardless of platform, and let the schema/`robots.txt` tactics apply fully only where you actually own the domain. ![Structured data distribution across owned web properties](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/xobsofohyefwrq57pkq2.png) ## Is GEO worth your time? Yes, but not the version most people are selling you. I recently rebuilt my own blog-writing process around exactly this, direct-answer blocks first, real stats with sources, extractable sections, and it's honestly made the drafts read better for humans too, not just for whichever bot happens to crawl them. I wrote up [the whole build here](https://www.swapnoneel.site/blog/make-ai-write-in-your-voice), if you want the longer version. ## FAQ **Do I need to block GPTBot to protect my writing?** That's a separate decision from GEO. Blocking `GPTBot` stops your content from training future models, but blocking `OAI-SearchBot` too would also stop you from showing up in ChatGPT's live search results. Decide which trade-off you actually want. **Will GEO replace SEO for blogs?** No, they overlap more than they compete. Structuring for extraction and citing real sources helps you rank in Google too. Think of GEO as SEO with an extra, stricter bar for evidence and standalone clarity. **How long before I see actual citations?** I don't have a clean number for this yet, since I only rebuilt my own process around it in mid-2026. Perplexity re-crawls constantly, so that's the faster feedback loop; ChatGPT search is slower and more selective about which pages it trusts. **Do I need schema markup if I only publish on Hashnode or Dev.to?** Not directly, since the platform controls that layer. Focus your energy on content structure and canonical URLs instead, those travel with you no matter where you publish. ![Thank you graphic for GEO for Developers guide](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/u9xplxu74ikwfrltmxfi.png) If you're building or rebuilding your own writing process around this, I'd genuinely love to hear what worked for you, drop it in the comments. You can also find me on [X (swapnoneel123)](https://x.com/swapnoneel123) or check out more of my work at [swapnoneel.site](https://www.swapnoneel.site). ## How I made an AI Agent write in my voice URL: https://www.swapnoneel.site/blog/make-ai-write-in-your-voice Date: 2026-07-06T00:00:00.000Z Summary: You can make AI write in your voice, but a prompt won't get you there... Let's be honest, AI-written blogs have a certain... vibe. You know it, I know it, and your readers can smell it from the first paragraph. But here's my take: you can make AI write in your voice, just not with a "generic" prompt. What actually worked for me is an agent skill with three parts: a voice profile built from seven of my real writing samples, a kill list of AI phrases, and a feedback loop that turns my edits into permanent rules. And here comes the twist, the blog you are reading right now is the very first output of that system! ![Generic AI prompt output vs personalized AI voice system](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/28o1zintr6j86wowkvn0.png) So, let me walk you through exactly how I built it, and you can judge for yourself whether it sounds like a human or not. ## Why does AI writing sound so... AI? Before fixing the problem, let's understand it from the ground up. An LLM is trained on billions of documents, so by default, it writes like the average of all of them. That's where phrases like "in today's fast-paced world"s come from, and those perfectly balanced conclusions that never pick a side. It's not that the model is dumb. It's that the average of a million voices is no voice at all. And your voice is the exact opposite of average. It's the specific way you break grammar rules, and the things you're willing to admit that others won't. I've written multiple technical blogs for different startups including Keploy, Devbytes and many more, and have been blogging on Hashnode since 2023. So when I asked AI to draft posts "in my style" with a simple prompt, the result was always the same: grammatically perfect, structurally neat, and absolutely not me. ## So, can you actually make AI write in your voice? Well, yes. But you have to show it, not describe it. ![Extracting writing mechanics from real blog samples](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/3s98sgz7952omd3ajmg3.png) "Write in a friendly, conversational tone" gives everyone on the internet the same friendly, conversational tone. What you need instead is a system that extracts the mechanics of your writing from real samples, and then enforces them like rules. Mine has three parts. ### Part 1: The voice profile I gave the agent seven samples of my writing: two journey blogs, one tutorial, one opinion piece, one comparison, three cold intros, and a small questionnaire about my tastes. And these are not just "any" samples, three of them are my past works that was cherry-picked by the system. And the other four were literally the topics given to me by Fable 5, so that it can understand my writing style better. But here's the important part, the profile it built isn't a list of adjectives. It's mechanics: - My sentences constantly open with And, But, So, and Now (this exact paragraph included). - My posts move forward by asking the reader's next question, and then answering it. - Every big claim needs a personal receipt with a number, not a vague "many developers say". - At most two "!!" per post. Yes, it literally counts them. And one more thing: newer samples always outrank older ones. My 2023 writing had habits I've dropped since, and the system knows my current voice wins every conflict. ### Part 2: The kill list The second file is a banned-patterns list. Every AI-ism I hate goes there: "delve", "seamless", "game-changer", rule-of-three sentences, em-dash chains, hedged conclusions that refuse to pick a winner, and emojis (all of them, I don't use emojis in my blogs, period). The rule is zero tolerance. If a banned pattern shows up in a draft, the agent doesn't just delete it, it rewrites the sentence the way I would say it. ### Part 3: The feedback loop (this is the part that actually matters) Now, the first two parts get you maybe 80% of the way. The remaining 20% is where every "write like me" tool I've seen gives up. Here's my loop: the agent writes a draft, I edit it like I normally would, and then a second skill diffs my final version against the draft. Every meaningful change gets generalized into a rule. If I cut a long intro once, that's a hypothesis. If I do it twice, it gets promoted to a confirmed rule that every future draft must follow. ![Human feedback loop creating reusable writing rules](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/yv9s4xjb0kxh7d4tjhz7.png) And there's a hard cap of 30 active rules. Why? Because this whole system runs on a smaller, cheaper model, and a smaller model follows 30 rules well and drowns in 80. The intelligence lives in the files, not the model. ## But does it learn from every single edit? Well, no. And this was a deliberate design decision. A one-off change (fixing a fact, rephrasing something topic-specific) teaches nothing about my voice, so it gets logged and forgotten. Only patterns become rules. Otherwise the agent would overfit to whatever mood I was in during one editing session. There's also one rule I consider non-negotiable: the agent can never invent a story about me. All personal facts live in a single profile file, and if a post needs an anecdote that isn't in there, the agent has to stop and ask me. An AI confidently fabricating a personal memory in your published blog is so much worse than a boring paragraph. ## Does it actually work? Honest answer: I don't fully know yet, and I won't pretend otherwise. This post is literally draft number one. The feedback loop has learned exactly zero rules from my edits so far, because there were no edits before this. You are looking at the "before" photo. If you can tell which sentences I touched after the agent wrote them, tell me in the comments, seriously! And another honest admission: setting this up took me more effort than just writing 2-3 posts by hand (I literally wrote four new blogs as an assignment lol, so that the LLM can infer my writing style better). The payoff only makes sense because it compounds, every post I edit makes the next draft closer to me. ![Fact verification guardrail blocking hallucinated AI claims](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/pivin0jtpppbxla1ajho.png) But the direction feels right, and I'm clearly not alone in thinking this way. The dev community has moved past one-shot prompting: Peter Steinberger's viral post ("you shouldn't be prompting coding agents anymore, you should be designing loops that prompt your agents") pulled 6.5 million views in June 2026 and [set the timeline on fire for a week](https://explainx.ai/blog/loop-engineering-coding-agents-claude-code-guide-2026). And the [Hacker News discourse in 2026](https://www.developersdigest.tech/blog/what-hacker-news-gets-right-about-ai-coding-agents-2026) has shifted from shiny demos to making agents repeatable and trustworthy. A writing agent with a feedback loop is just that same idea, pointed at a blog. ## What are agent skills, anyway? If the term is new to you, let's zoom out for a second. An agent skill is basically an onboarding document for an AI. It's a markdown file (usually called SKILL.md) with step-by-step instructions, plus supporting files it should read, that a coding agent like Claude Code loads before doing a task. Think of it like the difference between telling a new intern "write a blog" and handing them your company's full writing playbook. And the beautiful part is that skills are portable and dumb-model-friendly. I have orchestrated multiple agentic workflows, and the lesson from there was the same: agents don't fail because the model is weak, they fail because the instructions are vague. ## FAQ **How many writing samples do you need to clone your voice?** Seven worked for me, but coverage beats volume. One sample per content type (tutorial, opinion, comparison, narrative) teaches far more than ten samples of the same type, because your voice changes with the mode. **Can this work with a cheaper model?** That's the whole point. The voice profile, kill list, and rules carry the intelligence, so a smaller model just has to follow instructions. Save the expensive model for building the system, not running it. **How do you stop the AI from making up facts about you?** One canonical profile file, and a hard rule: if the fact isn't in the file, ask the human. Never generate a personal claim from thin air. Accepting that AI can't do the entire job for you, and you have to keep yourself in the loop, creates the difference. **Does this replace writing?** No, and I don't want it to. It replaces the first draft and the SEO chores. The opinions and the final edit are still mine, and honestly, that's the part I enjoy anyway. Even the current sentence that you are reading right now, was actually inserted by me during the edit. ## So, should you build one? If you publish regularly, yes. Build the voice profile. Really do. But don't skip the feedback loop, because without it you've just built a fancy prompt that will drift back into AI-slop within three posts. And start smaller than I did: pick your five most representative pieces, extract the mechanics (not adjectives!), list ten phrases you'd never say, and make reviewing the diffs a habit. If you want a more detailed analysis about the system, just comment down below and I would be happy to help you all! I'll be sharing more about this system as the feedback loop matures, including the numbers on how many edits it actually takes before drafts start needing none. If you want to follow that experiment, you can find me on [X (swapnoneel123)](https://x.com/swapnoneel123) or check out my other works at [swapnoneel.site](https://www.swapnoneel.site). ![Thank you graphic for AI writing voice blog](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/mnf69thhecu599edac1q.png) And that's a wrap! Have you tried making AI write like you? What worked, and what came out sounding like a LinkedIn bot? I would love to hear your experience. Thank you for reading, and have a nice day ahead!! ## Kimi K3 and the Rise of Open Weight Frontier Models URL: https://www.swapnoneel.site/blog/rise-of-open-weight-frontier-models-kimi-k3 Date: 2026-07-23T00:00:00.000Z Summary: Kimi K3 just landed a 2.8T open weight model a hair behind GPT-5.6 Sol and Fable 5. Here's why open weight frontier models finally matter. Apparently everyone is talking about the launch of Kimi K3 right now. So I wanted to share my two cents on this, especially for those who consider this to be just a "cheap chinese model". Because honestly, that joke doesn't land well anymore. Here is the actual reason why I'm saying so. Moonshot AI released Kimi K3 on July 16, 2026, a 2.8 trillion parameter open weight model, and it landed at #4 on the Artificial Analysis Intelligence Index, just behind Claude Fable 5 and GPT-5.6 Sol, and ahead of Claude Opus 4.8. Full weights are dropping on July 27. Pricing is 3USD and 15USD per million input and output tokens respectively, a fraction of what the closed labs charge. That's why open weight frontier models are suddenly a real conversation and not just a budget footnote. ## The old belief was that closed labs own the frontier For most of the last two years, the assumption was simple: if you want the smartest model, you pay OpenAI or Anthropic, period. Open weight models were the budget option, good enough for chatbots and side projects, but never good enough for the actual frontier. But those walls started shaking when we got GLM 5.2 from Z.ai, just a few weeks ago. And then came Kimi K3, which is the moment that assumption stopped being obviously true. Not because it beats GPT-5.6 Sol and Fable 5 outright, it doesn't, but because the gap has gotten small enough that "just use the closed model" is no longer an automatic decision. According to Nathan Lambert's analysis on Interconnects, the gap between open and closed, and between US and Chinese labs, has shrunk from a debated 6 to 9 months down to something closer to 3 to 5 months ([interconnects.ai](https://www.interconnects.ai/p/kimi-k3-the-open-weights-escalation)). ![Proprietary AI wall vs open-weight model accessibility](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/n2oyhuh4p1el7u73bhrn.png) ## What's actually inside Kimi K3 The specs are genuinely wild. It's a mixture-of-experts model with 896 experts, and it only activates 16 of them per token, so despite being 2.8 trillion parameters total, the compute cost per token stays manageable ([kimi.com](https://www.kimi.com/blog/kimi-k3)). It ships with a 1 million token context window, native vision, and a new attention mechanism called Kimi Delta Attention. On raw benchmarks, K3 takes first place on Program Bench, SWE Marathon, BrowseComp, and Frontend Code Arena. Program Bench specifically jumped from 53.6 to 77.8 over its predecessor, a 45% jump ([wan27.org](https://wan27.org/blog/kimi-k3-benchmarks)). In blind developer testing on Arena, people preferred Kimi K3 over both Fable 5 and GPT-5.6 Sol for front-end coding specifically ([codersera.com](https://codersera.com/blog/kimi-k3-benchmarks-comparison-2026/)). ![Kimi K3 MoE architecture with active expert routing](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/8e30q00qh3ztb3x6cfzi.png) ## Does that mean the closed labs are done? Well, not entirely. GPT-5.6 Sol and Fable 5 still sit ahead on the general Intelligence Index, around 59 and 60 versus K3's 57. And Moonshot's own success became a problem within days. Demand strained their compute capacity hard enough that they had to pause new subscriptions. That's not a small footnote. Running a 2.8T model at scale is expensive even when you're the one giving the weights away for free, and it shows that "open" doesn't automatically mean "infinitely available." And here's the honest catch on price too. K3 spends way more tokens in reasoning. So, Sol and Fable both tend to get to an answer in noticeably fewer tokens than K3 needs for the same task, so once you look at cost per task instead of cost per million tokens, the gap almost closes. The sticker price makes K3 look like a steal, the actual bill at the end of the month is a lot closer. ![Cost per task comparison between proprietary and open-weight models](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/uinoqqnkifadoeg9a002.png) ## Why open weight frontier models actually matter to you I ship AI products for a living, and most of my projects live and die by which model I pick underneath them, and cost per task isn't some abstract line item for me, it directly decides whether a feature is worth shipping. When a model that's a few points behind on intelligence lands close on actual cost and you can self-host it once the weights are out, that's not a footnote, that's a real decision every team building on LLMs now has to make. I ran into a version of this same tradeoff when I wrote about [testing AI coding agents](https://www.swapnoneel.site/blog/testing-ai-coding-agents), model choice was never just about the leaderboard, it was about what actually held up under my own usage. And there's a bigger reason than cost. A model whose weights you hold cannot be shut off by someone else's pricing decision, rate limit, or policy change. That's the sovereignty argument people keep making about open weight models, and it stops being theoretical the moment your product depends on an API you don't control. ![API key dependence vs open-weight model ownership](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/ttfanykfdtcz6ice3x64.png) ## The verdict Open weight models are no longer the consolation prize. Kimi K3 is proof that you can be a handful of benchmark points behind the absolute frontier and still be the more rational choice for a huge chunk of real work, especially coding. GPT-5.6 Sol and Fable 5 are still the smartest models on the planet right now, and if you need every last point of reasoning, use them. But if you want to not depend on someone else's uptime, and you're fine with the actual bill landing close either way, going and actually trying K3 instead of assuming the closed model wins by default is worth your afternoon. That's just me though, and your workflow might be different depending on what you're actually building. ![Thank you graphic for open-weight AI blog](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/az7um68ztq915wabjbf0.png) If you're experimenting with model choice for your own AI products, drop a comment with which model you've moved to since K3 landed. ## Nobody's Testing AI Coding Agents Enough URL: https://www.swapnoneel.site/blog/testing-ai-coding-agents Date: 2026-07-24T00:00:00.000Z Summary: 40-62% of AI-generated code ships with flaws. Testing AI coding agents stopped being optional, here's what I learned being paid to do it. Code review used to be the part everyone complained about. Slow, nitpicky, the thing standing between you and shipping. And for a while, AI coding agents made it feel optional. The agent writes the code, the code compiles, the tests pass, ship it. But have you ever wondered, what does that actually look like once you zoom out to the whole industry, and not just your own repo? Not great. Somewhere between 40 to 62% of AI-generated code got shipped with security or design flaws by March 2026, and roughly one in five breaches this year traces back to AI-written code, [according to industry analysis on the verification gap](https://futurumgroup.com/insights/why-ai-coding-agents-need-an-independent-review-layer-trust-not-output-is-the-bottleneck/). Code generation got really fast. Verification did not pick up the same pace. Testing AI coding agents properly is where that gap actually lives, whether your team has staffed for it or not. ## What's actually breaking? Let's get specific. In late June 2026, security researchers at Adversa AI disclosed something called GuardFall, a shell-interpretation bypass that worked against 10 of 11 popular open-source AI coding and computer-use agents, [including Aider, Cline, Goose, and OpenHands](https://securityaffairs.com/194546/ai/guardfall-flaw-hits-10-of-11-popular-open-source-ai-agents.html). The agents were checking the raw command text for danger before running it, but bash rewrites that text through quoting, substitution, and expansion before it actually executes. So a command that looks harmless to the safety check can still detonate once the shell gets its hands on it. And only one tool in the survey actually held up! ![The check reads the label. It never sees what the parcel turns into.](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/4pd0cgpsjrqtwi6jdlro.png) And it's not just an edge case for people running agents locally. A scan of 5,600 vibe-coded apps already in production [found 2,000 highly critical vulnerabilities and 400 exposed secrets](https://digitalbiztalk.com/article/vibe-coding-is-killing-open-source-the-2026-developer-crisis), some of them exposing medical records and payment information. Georgia Tech's Vibe Security Radar tracked the trend line getting worse, and not better: 6 confirmed AI-generated vulnerabilities in January 2026, 15 in February, 35 in March. ![Even Kernel doesn't like where this evidence is pointing.](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/zsaehsmgv4kvekhp4cov.png) ## Why is this happening if the models got so much better? Well, that's exactly the part people get backwards. Better models didn't remove the need for verification, they just moved the bottleneck. Generating a solution stopped being the hard part a while ago. Deciding whether you can actually trust that solution is the hard part now, and [55.4% of enterprise decision-makers already name agent reliability and hallucination management as their top production challenge](https://futurumgroup.com/insights/why-ai-coding-agents-need-an-independent-review-layer-trust-not-output-is-the-bottleneck/). The code compiles, the tests pass, and reviewers still have to reconstruct what the change was even trying to do before they can tell if it's safe. ## I've actually had this job, and it's not glamorous I'm not writing this from the outside. I recently worked with an early-stage startup that's figuring out their PMF before going full-throttle. I was the first layer of internal testing for the product, which was a self-evolving super agent (keeping it a bit vague, can't reveal more than this lol), and my entire job was catching bugs before the core users on it ever saw them. I also built an internal tool which was an agent chain based on strict rules that pulled the agent's data logs and evaluated them to ensure that the self-learning from feedback and real-life scenarios aren't being hallucinated, and if the agent is trying to manipulate the guidelines itself. Also, I generated reports on latency and probable slowdowns, because "it seems to be working" isn't a testing strategy, numbers are. ![Someone has to stand between the output and the door. That someone had a job title.](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/9hvhj0y9fidrjdbapalx.png) Before that, I spent time at Keploy building and improving sample apps specifically to demo API testing. So when I say testing agent output is a real, staffable job and not a checkbox, that's not a hot take pulled from a headline, it's what I got paid to do. And to be fair to the agents themselves: they are genuinely fast, and genuinely useful. I use Claude Code, Antigravity and Codex daily, and I'm not about to pretend otherwise. The problem was never that the code they write is bad on average. The problem is that "on average" is exactly the wrong bar for security and correctness, because the failures cluster in the 5-10% you didn't specifically check. (And yes, this post was drafted by an agent skill I built, and I'm going to go through and edit it before it goes anywhere near publish. That's not irony, that's the actual point: the draft can get the facts and the structure right, but deciding which of my receipts actually belong here, and how hard to steelman the agents, is still a job for a human. Mine, in this case.) ## What should you actually do differently? Stop reviewing agent output the way you review your own code, and start reviewing it the way you'd review a fast junior developer's very first PR: assume competence, verify everything, especially the parts that touch execution. Concretely: never let an agent pipe raw, unreviewed strings into a shell without a real evaluator in between, GuardFall exists because teams assumed string-matching was enough. Budget actual human review time as a fixed cost of using these tools, not a nice-to-have. And track your own vulnerability trend line the way Georgia Tech tracked the industry's, because "it hasn't broken yet" is not the same thing as "it's fine." And testing an agent isn't only about catching bugs before they ship, it's also about watching what the agent quietly costs you over time. That's exactly why I built that internal latency-tracking tool in the first place, numbers on slowdowns catch problems long before a user ever complains. If you don't want to build that yourself, tools are starting to do it for you: I recently found [Bifrost](https://www.getmaxim.ai/bifrost) from Maxim AI, a gateway that sits between you and your coding agents and gives you latency, cost, and token usage in one dashboard, plus fallback logic for when a model starts misbehaving. Worth a look if you're juggling as many agents and LLMs as most of us are these days. Do use these agents. Really do, they're not going anywhere and they've earned their place in my own workflow. But treat testing them as the actual job, not the afterthought, because right now, for most teams, it still is one. If you're building your own testing layer for an AI tool, or you've been burned by one that didn't have one, I'd genuinely like to hear about it, drop it in the comments. I write more about agent tooling and building with AI at [swapnoneel.site](https://www.swapnoneel.site), including [how I built a self-improving writing agent](https://www.swapnoneel.site/blog/make-ai-write-in-your-voice), and you can find me on [X (swapnoneel123)](https://x.com/swapnoneel123). ![Your turn. What did your testing layer catch?](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/vr4zw9hox2yybrgbhzqs.png) ## Trying Bifrost: An AI Gateway That Simplified My Setup URL: https://www.swapnoneel.site/blog/trying-bifrost-ai-gateway Date: 2026-07-30T00:00:00.000Z Summary: I set up Bifrost locally with OpenCode, tested automatic model fallback and complexity-based routing, and it fixed how I juggle LLM providers. I try a lot of models daily, and I kept ending up with a separate API key for every model provider I wanted to test with my desired harness. So I decided to try [Bifrost](https://www.getmaxim.ai/bifrost) on my local machine, to see if it would actually fix that. Bifrost is a high-performance, [open-source AI gateway](https://github.com/maximhq/bifrost), built in Go. It puts multiple AI providers behind a single OpenAI-compatible API, and it does that with ultra-low latency, automatic failover, load balancing, and enterprise governance features baked in. If you've used LiteLLM before, it may sound familiar on paper, but the experience is pretty different in practice. LiteLLM is a Python library and proxy you configure and run yourself; Bifrost ships as a standalone Go binary with a full web dashboard baked in, so there's no separate observability stack to stand up just to see what's actually happening to your requests. That dashboard ended up being the thing I used the most, as you'll see below. I wired it into [OpenCode](https://opencode.ai), an open-source coding harness similar to Claude Code and Codex. I'm using an OpenCode Zen key and a Gemini key, and together, these give me access to multiple SOTA models for free, without touching a separate dashboard for each provider. ## Installation and Setup The installation and setup was very simple, and quick. First, I installed the Bifrost CLI using this command: ```bash npx -y @maximhq/bifrost ``` Note: This requires Node and NPM to be installed on your machine, otherwise it won't work. If you want to know how to install and manage node versions, [you can follow this blog that I've written earlier](https://www.swapnoneel.site/blog/nodejs-npm-nvm). Now, it's time to run Bifrost! I'm doing it in a directory level, but you can also do it in a system level as well, if you want. For that, you can easily follow the [Bifrost documentation](https://docs.getbifrost.ai/quickstart/gateway/setting-up). ```bash npx -y @maximhq/bifrost -app-dir ./my-bifrost-data ``` I ran this command in my working directory, and this will create the configuration files and the logs db using SQLite. And also, this will expose our dashboard in port 8080, and from there we can easily set our API keys and use them in our applications, which I will get to you later in this blog. ## Connecting Your Providers Now it's time to grab your API keys, and connect it to Bifrost. As I've mentioned previously, I will be using Zen and Gemini. Both of these have generous free tiers, and getting the API keys doesn't require any credit card details. ![Adding an OpenCode Zen key in Bifrost, with allowed and blocked models configurable per key.](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/ifxbew6g3hran2gduwhf.png) As you can see from the above screenshot, adding your keys is pretty simple. You just need to select your provider, assign a name to your key and you are good to go! Additionally, you can also add allowed models to your specific key, because most of these keys come with a lot of available models, and if you don't want to use all of them, or limit your pool, you can do it from here as well. ## Integrating With OpenCode Now that you have connected your providers to Bifrost and it is already running, we can now integrate Bifrost and these models directly to OpenCode. Now, adding a connection like Bifrost means you have to manually edit the config file. Based on your operating system, the location of the config file might vary. More detailed info about that you can check on OpenCode's official documentation. But the file that we need to edit is `opencode.json`. In the provider block, we have to add something similar to this: ```json "bifrost-local": { "npm": "@ai-sdk/openai-compatible", "name": "Bifrost Local", "options": { "baseURL": "http://localhost:8080/v1" }, "models": { "opencode-zen/big-pickle": { "name": "Big Pickle via Bifrost" }, "gemini/gemini-2.5-flash": { "name": "Gemini 2.5 Flash via Bifrost" } } } ``` Note: I gave this its own `bifrost-local` provider block instead of just pointing OpenCode's built-in `openai` provider at Bifrost, the way Bifrost's own docs show it, because I wanted it visually obvious in the model selector which models are going through Bifrost versus hitting a provider directly, in case I ever wire up a real OpenAI key in the same config later. Either approach works functionally; this is just how I like to keep them apart. Now this will vary based on your selected model and provider. It is better if you can check it out from the Bifrost docs itself. Now that we are connected, we can launch OpenCode from our terminal (or app), and in the model selector we will see something like this: ![Both models now show up in OpenCode's model selector, routed through Bifrost Local.](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/si2cvhaifiesppmkgg4m.png) Now we can select this model and use it to do our task on OpenCode. But wait, till now I just described how Bifrost sits between your model provider and harness. So, let's get to the crux and find out what more things we can do with Bifrost! ## Monitoring and LLM Logs Bifrost gives us a clear understanding of how our models are performing, the amount of tokens they are consuming, the latency and the cost as well. In the screenshot below, you will be able to see how Bifrost does that: ![Bifrost's live LLM logs: requests, success rate, latency, tokens, and cost, all in one dashboard.](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/5gy16zixrg4t5slxic89.png) We can see and check the detailed logs as well. All of your data is stored locally and nothing gets sent to the cloud, other than the messages we're sending to the model providers, which is obvious. Everything is kept air-gapped. ## Model Routing using Automatic Fallback Now the most interesting part: how do we automatically route the models? During production, one model might fail to respond, and it's kinda common. So for those scenarios, we can set rules like: If model A is not available, then use model B. This is the simplest version though. Using CEL expressions, we can create custom routing rules for almost anything that we want. If models are available and we have a specific logic in mind, we can implement that in Bifrost easily. So, for now, let's create a simple rule, such that: if Gemini models aren't available, we will route the traffic through Zen models instead. ![The fallback rule: if Gemini's gemini-2.5-flash fails, route to OpenCode Zen's Big Pickle instead.](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/ug7deh69apmje8fb8h8m.png) As you can see in the screenshot above, I have created a global rule with maximum priority, such that if the provider is `gemini` and the model is `gemini-2.5-flash`, and if the user is using that specifically, we will fall back to `opencode-zen/big-pickle` instead. Now as the rule is set and applied, I will temporarily disable the gemini services to see what happens. Let's come to OpenCode, and type a "hi", and let's see what happens. ![Sent a quick "hi" from OpenCode with Gemini disabled, no visible interruption.](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/y7rxucfli3ivtomvff6i.png) As you can see, the user didn't get any interruption at all. Let's check the logs to find out what actually happened here. ![The logs confirm it: Gemini errored out, and Bifrost silently fell back to big-pickle.](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/3njozpqr6g3ytrb177ar.png) As we can see, the gemini model gave an error, and it automatically fell back to big-pickle, and gave me the response. That's the magic of Bifrost. ## The Complexity Router Now that we have understood Model Routing, let's see how we can determine and set what kind of requests should go to which kind of models. For example, we can route trivial queries to cheap models, while preserving the expensive ones for difficult tasks. The idea is simple: divide the incoming requests into four tiers: simple, medium, complex and reasoning. So I've configured my complexity routing profile, and you can easily do it as well based on your own requirement. Here's my profile: ![My complexity routing profile: tier boundaries and keyword lists that decide simple, medium, complex, and reasoning requests.](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/jn984noo2rlwqhqq6b3q.png) For my simple and medium queries, I want the gemini model to handle that, and for complex and reasoning-based queries, I want them to go to big-pickle. Now, we need to create custom routing rules for that. ![The two complexity routing rules: simple and medium traffic to Gemini, complex and reasoning traffic to Big Pickle.](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/f0w7uf10mauqav9dopjv.png) Now that the rules have been created, let's test them, and check the LLM logs. I'll send two queries from OpenCode: - `Hello, briefly define REST API.` (expected to go to gemini) - `How to debug an async API authentication failure step by step, explain the root cause, and recommend an architecture fix.` (expected to go to big-pickle) ![The simple query went to Gemini, the complex one to big-pickle, exactly as configured.](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/8axw0rg5qpcc7kx5dtwq.png) And we can see in the screenshot above that it worked exactly how we intended. ## Bifrost Can Do a Lot More Model routing and the complexity router are the two features that got me the most excited, but Bifrost isn't limited to just these two. It also ships guardrails, virtual keys, and cluster mode for scaling across machines, and that's still on my list to explore. Honestly, I loved this. Setting it up took maybe fifteen minutes end to end, and it quietly fixed a problem I'd been living with for a while: juggling separate API keys and dashboards for every provider I wanted to test. The fallback and complexity routing worked exactly the way the docs said they would, no surprises, and that alone made this whole exercise worth it. ![Please like, share and follow](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/s436tk6aszvy9g079vjt.png) If you're juggling more than one model provider and keep swapping keys by hand, give Bifrost a shot. And if you've already tried it, let me know what you built with it, and also if the enterprise version intrigues you, you can [book a demo as well](https://www.getmaxim.ai/bifrost/book-a-demo). ## Why Vercel is still my default for shipping frontend projects URL: https://www.swapnoneel.site/blog/vercel-frontend-deployment-default Date: 2026-07-13T00:00:00.000Z Summary: Why I keep reaching for Vercel for frontend projects — and where Cloudflare, Netlify, and Railway are the better choice. Last week, I was working on a client project with a fast approaching deadline. The work had already piled up, so I had to move really fast; I was constantly making changes, pushing them straight to GitHub, checking them through the preview link of the deployment, and going straight to the next task. And while doing so, I barely stopped and worried about hosting, because Vercel was already connected. And after successfully delivering the project within the stipulated time, it hit me that I probably could not have moved that quickly if the deployment itself had been another thing to manage. That made me realise: Vercel has been my default choice for a long time, and it is not because I am completely locked into the platform. From time to time, I still reach for other services like Cloudflare, Netlify, and Railway as well, but for my personal projects and fast development cycles, I somehow always end up coming back to Vercel. ![Vercel automatic frontend deployment workflow](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/gcum98tsutf93kuwjt2v.png) I mostly use Next.js, so I know the tech nerds out there will assume that, it is the entire reason why I choose Vercel, and that's a fair assumption to make, because it's partly true. Vercel develops and maintains Next.js, so of course it provides the best hosting for Next.js, but that's just one side of the coin. ## How I use Vercel in my projects If you check the projects section on [my portfolio](https://www.swapnoneel.site/work), you will find that most of the web projects I currently have are deployed through Vercel. And not all of them are Next.js applications; you will find projects with React, TanStack tooling, Node.js, and Bun as well. These are not just weekend experiments or hobby projects, either. Some of them have real users as well! Let me give you [Scholarian](https://scholarian.vercel.app) as an example. It is a research platform built on Next.js, and according to my latest project analytics, it currently has more than 75 active users and over 700 chat sessions. The funny thing is that I did not think twice about deploying most of these projects. I connected the repository, gave Vercel the required environment variables, and pushed the code. That absence of thought is the whole point. But then again, I also use Cloudflare Pages and Railway for actual work, so this is not a “Vercel is perfect and everything else is bad” argument. I have reasons for coming back, but I also know where the platform starts becoming the wrong tool. So, let's discuss! ## Why do I keep coming back to Vercel? **First of all, Vercel's preview deployment workflow!** It makes my development cycle much smoother. By default, every non-production branch can receive its own preview URL, and I can share that URL before merging the branch. That's extremely useful for catching visual problems before they reach production. A pull request may look completely fine during code review, but you can never know when the actual interface breaks at a particular viewport width. This has happened to me a lot. Just a few days ago, I shipped the near-final version of a project to one of my clients without noticing that, in the mobile version, a heading was overlapping one of the image assets. Preview deployments let people test the thing instead of trying to imagine it from a diff. ![Vercel preview deployment card sharing and visual QA](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/53o4fp681781ercsv3cd.png) For a solo developer and freelancer like me, this saves a lot of time because, as you can see in the below screenshot, Vercel adds a toolbar to preview deployments where collaborators can leave comments directly on the page. This was especially useful during hackathons, when we were short on time. And our team always communicated in that way, and my teammates would drop in and leave comments like "the link to this button is redirecting to the pricing page instead of the features page" or "the color is way too contrasty." The small catch is that they need a Vercel account to comment, and external collaboration has some plan-specific limits, so it is not entirely frictionless, but still, it is much easier than sending Loom videos, annotated screenshots, or five messages explaining which button or font your client or peers want. And they have an optional third-party integration as well that can convert a preview comment into a GitHub issue. This makes conversations with my clients and non-technical collaborators much easier! ![Vercel preview toolbar with comments and collaboration controls](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/8skbo3bygfl4pfwmfo0o.png) **The Next.js experience is the other reason I keep using it.** Vercel develops the framework, so features such as Incremental Static Regeneration, Server Actions, React Server Components, route handlers, and streaming work with very little platform-specific configuration, and I don't have to spend an afternoon figuring out how a new Next.js feature maps onto the hosting environment. Vercel covers that part for me by default. Now, to be fair, other platforms have improved a lot, and Netlify currently supports the major Next.js features through its OpenNext adapter, including Server Components, Server Actions, streaming, ISR, and Partial Prerendering. Cloudflare can also run Next.js using its own OpenNext-based adapter. So the difference is no longer that Next.js features simply do not work elsewhere, because that would be an outdated argument. The difference is that Vercel remains the first-party deployment target, and that means there is one less compatibility layer to worry about. And this removes a pain point for me, especially when I am using a newer framework feature. And that's the edge I'm actually talking about. For a normal static React or Vite application, this advantage matters much less, but for a serious Next.js project, it becomes my go-to option. ![Vercel Analytics dashboard showing visitor traffic and bounce rates](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/v8qp2p0klx6l09y43ly7.png) And then there's the DX at the dashboard level. These are minute things, but together, they make a big difference for me. For example, the environment variables are scoped per environment (local, preview, and production; all of them are isolated). Rolling back to any previous deployment takes two clicks. And the deployment logs actually tell you what failed, not just that it did, and because of that, they become much easier to fix if you are taking the “pasting it into Claude Code” route. ## What are the alternatives to Vercel? Well, when we are talking about the alternatives, **Cloudflare Pages** is the one that comes the closest. And we know how much tech Twitter is divided on this one, and how frequently we see their representatives fight each other on open threads regarding this (I enjoy watching those heated arguments, lol). And yeah, Cloudflare is genuinely fast, and [their edge network spans 300+ locations](https://www.cloudflare.com/network/), and for static content, the performance gap over Vercel is actually quite measurable. And what I appreciate most is that the pricing is much more predictable; because, first of all, there are no egress fees, and they also provide unlimited bandwidth on the free tier. And as a bonus, I have also seen them [helping start-ups from time to time as well](https://x.com/IanLandsman/status/2059289714264273337), which is a great initiative, in my opinion. ![Infrastructure control vs fast shipping speed comparison](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/j0aqe0r43weglq9cpqb3.png) I respect all of this, but the problem is that Cloudflare's Workers environment runs on V8 isolates, which is different from a standard Node.js runtime. And this is where I face the most problems. For purely static sites or projects that have lightweight edge functions, it's totally fine, but sometimes, with specific packages that exclusively require a Node.js runtime, you start to face error messages. And although `nodejs_compat` mode now supports a substantial portion of the Node API, the compatibility is still not perfect. There is also a trade-off in how the two platforms approach infrastructure. If you want databases, KV stores, or smart routing in your project, you must understand Cloudflare's broader ecosystem, like D1, KV, and routing rules, which is great when you want that level of control. But Vercel abstracts all of that by default. It is basically a trade-off of infrastructure control for speed, and I prefer Vercel's simpler deployment workflow in this regard. **Netlify** was my original platform before I switched. I have nothing against it, honestly. It is very similar to Vercel in a lot of ways. But Vercel's integration with Next.js, which I just discussed in detail in the previous section, makes Netlify feel like it's one step behind. Features like Server Actions and React Server Components work natively on Vercel, while on Netlify, they have to go through adapters that often lag behind new framework releases, which is a big compromise. And another thing: Netlify's core CDN infrastructure also has fewer edge locations than Vercel's 100+ node network, and that's visible in the global TTFB numbers as well. I'd still use Netlify for a simple static site with a form or two because their built-in form handling is actually clever. But for a Next.js project, Vercel is my primary choice. Now, for **Railway**, it's a bit different, and I use it when I need a persistent backend, like maybe a WebSocket server, a background job, or something that can't be serverless. In Scholarian, I have a long-running task where the background worker has to produce a long report using Gemini, and that process generally takes three to four minutes, so I switched the backend of my app to Railway. For Vercel, that's where it genuinely breaks down. If you need a long-running process, you're either doing something hacky with edge functions or you're reaching for a different platform. And there are a couple of good options besides Railway, and for that, [Encore](https://encore.dev) would be my personal recommendation. ## Where Vercel actually falls short **The pricing model!** And that's the part I like the least. The free Hobby plan is capped, so it cannot generate an on-demand surprise bill, but if a hobby project exceeds its allowances, it may be paused or restricted instead. That's why I keep an eye out for my portfolio site, because that's the one that gets the most traffic. The bigger billing concern begins as soon as you switch to the Pro plan, where usage beyond the included credit can be charged across multiple resources. Being mindful enough is particularly important here, because I have seen a lot of posts on Reddit and X where developers have complained about the same issue. Vercel Pro currently has a 20 USD monthly platform fee, which includes one deploying seat and 20 USD of usage credit. And for additional developer seats that can deploy or configure the projects, they will cost you another 20 USD per month, but the read-only viewer seats are free. And that combination can become difficult to predict when a project grows. Vercel provides spending alerts and lets paid teams configure actions such as pausing projects after reaching a limit. Hence, it's better to enable those controls instead of assuming that traffic will always stay predictable. Sudden bot traffic, a poorly optimized function, image transformations, or a sudden spike in legitimate users can all consume usage faster than expected. So, it's always better to keep those factors in mind so that you don't get overcharged accidentally. **The other limitation is compute.** Vercel Functions can handle APIs, server-rendered routes, streaming, and other request-driven tasks, and the current function limits are far more generous. But if your application requires a continuously running background process or custom Docker containers, Vercel isn't the right fit. There are platforms like [Render](https://render.com) or [Northflank](https://northflank.com) that are built for that kind of workload. Vercel is a frontend cloud, so the moment you need full-stack infrastructure, you're pairing Vercel with something else anyway. Hence, the title of my blog says why I prefer it for frontend projects, and not full-stack projects! And then there is also vendor lock-in, although I do not think it is as simple as people make it sound. And it's not limited to Vercel either; almost every service provider has its own kind of vendor lock-in. A static React or Vite project is easy to move, but a Next.js application that depends heavily on Vercel’s caching behavior, image optimization, routing, integrations, and deployment settings will take more effort to migrate. The more platform-specific behavior you adopt, the less portable your application becomes, and that's true for Vercel, Cloudflare, AWS, and almost every other cloud platform. I have not experienced the issue myself, but I have seen people complaining about it, so I included it in the blog. ## Is the Vercel free tier actually good enough? For most side projects, yes! And their [Hobby plan](https://vercel.com/pricing) is free for personal use and is pretty generous. It comes with unlimited projects, automatic HTTPS, custom domains, preview deployments, and 100 GB of bandwidth per month, and that's mostly enough for personal use. And I never felt the need to purchase their paid Pro plans. But the moment you add a team or need more bandwidth or function execution, you have to go to the Pro plan that starts at $20/month for each member. ## What is the verdict? Vercel is still my default for frontend deployment and a part of my development cycle. The preview URL workflow alone has saved me more debugging cycles than I can count. I've tried the alternatives in actual projects, and none of them gave me back the time I was spending on deployment issues. That said, if you're cost-conscious and don't mind the learning curve, then Cloudflare is still a great choice. And if you need a backend, pick Railway and point your Vercel frontend at it. And if you've moved away from Vercel for something specific, or if you have a setup that works better for you, I'd genuinely love to hear about it in the comments! ![Thank you graphic for Vercel deployment blog](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/tqs4v53py7hbpl3fdtgz.png) And if you want to see the kinds of projects I've actually shipped on Vercel, check them out at [my site](https://www.swapnoneel.site). Also, you can find me on [X](https://x.com/swapnoneel123) or [GitHub](https://github.com/Swpn0neel) if you want to talk or connect. ## What is Adaptive Load Balancing, and Why AI Needs It URL: https://www.swapnoneel.site/blog/what-is-adaptive-load-balancing Date: 2026-08-17T00:00:00.000Z Summary: Adaptive load balancing routes by live health, not a fixed rotation. How the scoring actually works, and why AI traffic breaks the older algorithms. Five identical servers sitting behind one load balancer, each getting exactly one-fifth of the requests. So why is one of them pinned at 90% CPU while another one sits half idle? Well, because an equal share of requests is not an equal share of work. **Adaptive load balancing** is a routing strategy that picks a destination using live health signals like error rate, latency and utilization, instead of a fixed rotation. The balancer keeps scoring every backend while traffic flows, shifts weight toward the ones behaving well, and pulls weight away from the ones going bad. That's the definition. But the definition is the boring part, so let's get into what the balancer is actually measuring, how fast it reacts, and why this suddenly matters a lot more in 2026 than it did five years ago. ## What is a load balancer? A load balancer is just a thing sitting in front of your servers, deciding which one gets the next request. That's it. The simplest version is round robin. Request 1 goes to server A, request 2 to server B, request 3 to server C, then back to A again. It's a rotation, and it never once looks at what is actually happening inside those servers. Static algorithms like this quietly assume two things: every request costs the same, and every server has the same capacity right now. Both assumptions survive about five minutes of real production traffic. ![Equal turns can still create unequal work](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/bltwhnlm4r1ooiqa9pvt.png) Think of a supermarket. Round robin is the sign saying "next customer to the next till, in order." Adaptive is a floor manager who watches which till is genuinely moving, spots the one stuck behind a price check, and sends people elsewhere. Same queue, very different Saturday. ## How does an adaptive load balancer decide where to send a request? It collects signals, turns them into a score per route, and turns those scores into weights. Three signals do most of the work: 1. **Error rate.** Is this backend returning failures? Usually the heaviest signal, and usually time-decayed, so a spike from ten minutes ago stops dominating the decision. 2. **Latency.** How slow is it right now, both against its peers and against its own recent baseline? A route that always takes 2 seconds is fine. A route that usually takes 200ms and is now taking 2 seconds is in trouble. 3. **Utilization.** How much of its capacity is already committed, so that no single fast route gets hammered into becoming a slow one. Those collapse into one number per route, and a higher number means a bigger share of the traffic. And the weights are not recalculated per request, because that would drop real work onto the hot path. They get recalculated on a background loop, and each incoming request simply reads the numbers that were computed a moment ago. ![Live signals become route weights](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/tpeg1tbewphirbc8s1pa.png) [Bifrost](https://www.getmaxim.ai/bifrost), the open-source AI gateway from Maxim, is the clearest published example of this that I have come across. Its adaptive load balancer scores routes on error penalty (50% of the score), a token-aware latency score (20%), and utilization (5%), plus a momentum bias that speeds up recovery once a bad route starts behaving again. Weights recalculate every 5 seconds, and route selection adds under 10 microseconds to the hot path, [per its documentation](https://docs.getbifrost.ai/enterprise/adaptive-load-balancing). The entire source code is on [GitHub](https://github.com/maximhq/bifrost), so you can go read how the scoring is implemented rather than taking a feature page's word for it. Most load balancers describe their algorithm as "intelligent" and then stop talking. The other half of the mechanism is state. A good adaptive balancer doesn't just have a dial, it has an opinion about what each route currently is: healthy, degraded, failed, or recovering. And the recovering state is the one people forget. A route that failed does not get cut off forever, it gets a thin trickle of live traffic so the balancer can find out when it is better. Without that, your balancer is just a fancy circuit breaker that never closes again. ## Adaptive load balancing vs round robin and least connections Everyone puts these in a table. I would rather just tell you where each one stops working. **Round robin** rotates blindly. Fine when every server and every request is genuinely identical, which is basically never. **Weighted round robin** lets you say "server A is beefier, give it double." Better, but you set those weights by hand, based on what was true when you deployed. It has no idea what is true at 3am during a traffic spike. **Least connections** picks whichever server has the fewest open connections. This one is genuinely dynamic, and it is a solid default. But an open connection is a rough proxy for load, since one connection doing heavy work counts exactly the same as one connection idling. **Least response time**, usually implemented with an exponentially weighted moving average of latency, gets close to adaptive. It measures the thing you actually care about. Adaptive load balancing is the version that stops relying on any single number. It combines errors, latency and capacity, keeps a health state per route, and has explicit behavior for pulling a route out and easing it back in. Does the extra machinery pay off? Envoy's own benchmark for its Peak EWMA policy puts it at a 99.9% success rate under a 1-second timeout, against 99% for least-loaded and 95% for round robin ([Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/contrib/load_balancing_policies/peak_ewma/peak_ewma)). That gap between adaptive and round robin is made entirely of user-visible failures. ## Alright, so far this is a decades-old idea And it genuinely is. Adaptive load balancing has been in networking gear and reverse proxies forever, and if you run a normal web app behind NGINX, least connections is probably good enough and you can stop reading here. So why is the term suddenly everywhere again? Because AI traffic breaks nearly every assumption the older algorithms were built on. That is where this stops being system-design trivia and starts being your on-call pager, so let's get into it. ## Why LLM traffic makes static load balancing fall apart? ![Large token loads expose static routing limits](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/rlgf0fpspsc35cdk60qi.png) Four things go wrong at once. **Requests are wildly different sizes.** A "summarize this sentence" call and a "read these 40 pages and reason about them" call hit the same endpoint, and one of them costs a hundred times more. When I built [Scholarian](https://scholarian.vercel.app), a deep-research pipeline over academic papers, it ended up fetching and ranking over 10,000 papers across 250+ search sessions. Some sessions were one cheap query. Some were a long chain of very expensive ones. A rotation cannot tell those apart, so it cheerfully fires the expensive one at the route that is already drowning. **The limits are not counted in requests.** Model providers rate-limit you on requests per minute _and_ tokens per minute, and it is usually the token ceiling you hit first. So a balancer counting requests is watching the wrong meter, and you find out about it through a 429 error in production. **The backends are not yours.** You cannot SSH into OpenAI. There is no CPU graph, no memory reading, nothing except the latency and error rate you observe from outside. Observed behavior is the only signal you have, and observed behavior is exactly what adaptive balancing runs on. **Every API key is its own bottleneck.** Rate limits are per key, so teams end up holding several keys per provider. Now you are not balancing across servers anymore, you are balancing across a grid of providers and keys, each with separate limits and separate health. ## What adaptive load balancing looks like inside an AI gateway? ![Provider selection followed by API key selection](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/c8hkbqxrhshh2kazha4j.png) The gateways handling this properly split the decision in two. The first level picks the provider and model for a request, based on live capacity and error rates across all of them. The second level picks which API key inside that provider actually gets used, weighted by how each individual key is performing. That two-level shape matters because the failures are different at each level. A provider goes down for everybody at once. A single key just quietly hits its own token ceiling while its siblings are perfectly fine. One balancer trying to handle both would be making the wrong call half the time. I went through the practical side of this in my post on [Bifrost's enterprise features](https://www.swapnoneel.site/blog/bifrost-for-enterprises), where the adaptive routing sits right next to audit logs and guardrails. Worth a read if you want the version with an actual dashboard in front of you. ## When you should not reach for adaptive load balancing ![Sparse signals and weak capacity call for caution](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/3e6j3ogmivqjmcrf5vl8.png) Now the honest part, because none of this is free. **You need real telemetry before you need adaptive routing.** At a contract role earlier this year I built an internal tool that captured our AI product's logs and generated reports on latency and probable slowdowns, and the uncomfortable lesson was that most of the wins came from simply _seeing_ the numbers. Half the routing problems people want an adaptive balancer to solve turn out to be one bad prompt template or one undersized instance, and a dashboard finds those faster than an algorithm hides them. **Low traffic means no signal.** Scoring on error rate and latency needs enough requests per window to mean anything. At 5 requests a minute, an adaptive balancer is mostly reacting to noise, and reacting to noise is worse than not reacting at all. **It can paper over a capacity problem.** If every route is degraded, adaptive balancing will smoothly and confidently send you to the least-bad option, forever, while the real answer was "add capacity" or "get off the free tier." **And it is one more moving part.** More state, more tuning, one more thing to reason about at 3am. If round robin across two identical boxes is working for you, keep it. ## So what's the final message? If you run a plain web service on infrastructure you control, least connections is fine and adaptive load balancing is over-engineering. If you are routing to model providers, it is not optional anymore. You are balancing across backends you cannot inspect, with limits measured in tokens, with per-key ceilings, and with failure modes that arrive as a slow degradation instead of a clean crash. A fixed rotation has no mechanism to even notice that. Use a gateway that scores routes on live behavior and moves the traffic for you, and go spend that attention on your product instead. And also, if what Bifrost is doing intrigued you, you can easily [book a demo](https://www.getmaxim.ai/bifrost/book-a-demo), and see how it fits in your organization. ![Simple services can stay simple; AI traffic needs adaptation](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/tjxt9en3l4yzf1gelqlr.png) That is my take, and your setup might look nothing like mine. If you have built this kind of routing yourself, or you have watched an adaptive balancer make a genuinely stupid decision, drop it in the comments, I want to hear it. You can find me on [X](https://x.com/swapnoneel123) where I post about most of what I am building, and the rest of my writing lives at [swapnoneel.site](https://www.swapnoneel.site). ## What Is Semantic Caching, and Where It Quietly Breaks URL: https://www.swapnoneel.site/blog/what-is-semantic-caching Date: 2026-08-19T17:39:37.000Z Summary: Semantic caching serves cached LLM answers to queries that only match in meaning. How it works, the threshold trap, real hit rates, and when to skip it. Two people open your support chatbot within the same minute. One types `How do I reset my password?` and the other types `i forgot my password, how do i get a new one`. Same question, same answer, two full model calls, and your Redis cache stores both as separate keys without ever hitting on either. **Semantic caching** stores past LLM responses and serves them to new queries that mean the same thing, even when the words are completely different. It works by turning every query into a vector, searching for the nearest stored vector, and returning that cached answer if the similarity clears a threshold you set. Now, let's understand how it actually works! ## Why your existing cache does nothing for LLM traffic Every cache you have ever written works on exact equality. You take the request, hash it, look up the hash, and either the bytes match or they don't. Redis and Memcached both work this way, and so does the HTTP layer sitting in front of them. It works brilliantly. It works because the traffic it was designed for is machine-generated. `GET /api/users/42` is always spelled the same way by the same client, every single time. ![Exact matching misses equivalent questions](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/3v3qhwmra038m9m4xgsp.png) Human language is not like that. There are roughly infinite ways to ask for a refund policy, and a hash function treats every one of them as a different universe. One extra space, one lowercase letter, one "please" at the end, and you get a completely different key. So your hit rate on natural language collapses to nearly zero, and you go on paying for the same answer over and over. Semantic caching fixes the matching function instead of the cache. The storage stays boring. What changes is that you stop asking "are these two strings identical" and start asking "are these two strings close enough in meaning". And that one word, **close**, is where all the difficulty in this topic lives. ## How does semantic caching actually work? The whole thing is five steps, and none of them are complicated on their own. Before any code, here is the rule in plain English: *Turn the question into a point in space. Look for the nearest point we have already answered. If it is near enough, reuse that answer.* Now the actual shape of it: 1. A query comes in. 2. You send it to an embedding model, which returns a vector of floats. Usually 768 or 1536 dimensions, depending on the model. 3. You search your vector store for the nearest stored vector, using cosine similarity. 4. If the best match scores above your threshold, you return the stored response and never call the model at all. That's a cache hit. 5. If nothing clears the threshold, you call the model, return the real answer, and write the query vector plus the response into the store with an expiry time. ![The five-step semantic caching pipeline](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/1qpab5iqbnfib7t76sfx.png) In code it is almost insultingly short: ```python def ask(query, threshold=0.92): vec = embed(query) # step 2 match, score = store.nearest(vec) # step 3 if score >= threshold: # step 4 return match.response # cache hit, zero model tokens answer = llm.complete(query) # step 5, cache miss store.put(vec, answer, ttl=3600) return answer ``` Notice what step 4 is really doing. It is taking a floating point number and using it to decide whether a human being gets a fresh answer or a recycled one. There is no other logic in this system, no parsing and no intent classification, nothing else that ever looks at what was actually asked. If you want the mechanics of what `store.nearest` is doing underneath, I built one of these from scratch, cosine similarity and the HNSW graph and all, in my post on [building a vector database from scratch](https://www.swapnoneel.site/blog/build-vector-database-from-scratch). The short version is that it is an approximate nearest neighbour search, so it is fast, and it is also allowed to be a little bit wrong. ## The similarity threshold is the whole product ![One threshold, two opposing failure modes](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/vx141iz8mc6jumndcpk8.png) Let's do something most articles on this topic skip. I'll give you three pairs of queries, you predict what a cosine similarity score should look like for each, and then we will check against what embedding models actually return. Pair one. `What is your refund policy?` and `Can I get my money back?` Different words entirely, same intent. This one **should** score high, and it does. This is the case semantic caching was built for, and it works. Pair two. `Show me the sales numbers for Q1 2025` and `Show me the sales numbers for Q3 2024`. Almost identical strings, completely different answers. You would want this to score low. It does not. It scores extremely high, because most of the tokens are shared and the embedding barely notices which quarter you asked about. Pair three, and this is the one that should worry you. `Is this drug safe for pregnant patients?` and `Is this drug not safe for pregnant patients?` One word apart, opposite meaning. Here's what actually happens. Embedding models are largely blind to negation, and this is not a rumour, it is measured. A [validity audit published in August 2026](https://arxiv.org/html/2608.10216) tested 9 encoder configurations and found that negation and antonym pairs score **above** genuinely similar pairs on every model tested, at average cosines of **0.93 to 0.999**. In the production system that paper audits, flipping an instruction from "withhold the study drug" to "administer the study drug" scored **0.9608**, and the safety gate that existed specifically to catch that never fired. Read that once more. The reversed instruction scored higher than most legitimate paraphrases would. So the prediction most people carry into this, that a threshold like 0.92 cleanly separates "same question" from "different question", is just wrong. What the threshold separates is **surface form**, and surface form is not meaning. Two sentences that share a grammatical frame and differ in one date, one entity, or one negation will sit above almost any threshold you are willing to set. That's the trap. Raise the threshold to 0.98 and you kill your hit rate, because honest rephrasings stop matching. Lower it to 0.85 and you start serving Q3 2024's numbers to someone asking about Q1 2025. There is no single number that fixes both, because the failure is in the measurement and not in the cutoff. What actually helps is refusing to let similarity be the only gate. Partition the cache by anything the embedding is bad at holding: user, tenant, model, and any structured parameter your queries carry. If dates and IDs are pulled out into the cache key instead of being left sitting inside the prose, the embedding never gets a chance to blur them. ## Semantic caching vs prompt caching vs KV caching ![Three caching layers at different depths](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/o2albu3d51p4reqzb2w0.png) These three get used interchangeably online and they are three completely different layers. Getting this straight is worth more than any amount of threshold tuning. **KV caching** lives inside the GPU. During inference the model computes key and value tensors for every token in your context, and the KV cache keeps them around so the next token does not need to recompute attention over everything before it. This is always on, you do not configure it, and it is the reason generation gets faster after the first token. **Prompt caching**, sometimes called prefix caching, is what OpenAI and Anthropic sell you at the API level. It reuses those KV tensors across requests when two requests share a common prefix. So if you send a 4,000 token system prompt on every call, the provider can skip recomputing it and charges you less for those tokens. Important detail: it matches on **exact prefix bytes**, so two prompts that mean the same thing but start differently will miss it entirely. **Semantic caching** sits in front of the model, in infrastructure you control. It stores whole request and response pairs and matches on meaning. When it hits, you save 100% of the call, because the model is never invoked. They stack, and they should. A request should try the semantic cache first, fall through to the provider's prompt cache on a miss, and only then pay for full inference. The savings are multiplicative rather than competing, and the layer you own is the outermost one. ## What is a realistic cache hit rate? ![Real-world hits are a minority of requests](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/u8og1qce297e7399xh0h.png) This is where I want to be blunt, because the marketing on this topic is bad. You will see 95% quoted constantly. Trace that number back and it almost never refers to hit rate. It refers to **match accuracy**, meaning the cached response was correct 95% of the time it was served. Those are entirely different claims, and the second one tells you nothing about how much money you saved. Actual production numbers are much lower. A [breakdown of real deployment data](https://dev.to/gauravdagde/llm-semantic-caching-the-95-hit-rate-myth-and-what-production-data-actually-shows-8ga) puts typical hit rates at **20 to 45%**, with Portkey seeing around 20% on retrieval-augmented workloads and an EdTech platform hitting about 45% on student question-and-answer traffic. Open-ended chat sits at 10 to 20%, because open-ended chat is genuinely open-ended. Academic results land in a similar band. The [GPT Semantic Cache paper](https://arxiv.org/pdf/2411.05276) reports cutting API calls by up to **68.8%**, but that is on query categories picked for repetition, which is exactly the workload where this technique looks its best. And 20 to 45% is still a very good deal! On a 5,000 dollar monthly bill, a 20% hit rate is 1,000 dollars back, and the latency win is bigger than the money win. A cache hit returns in under 5 milliseconds against 2 to 5 seconds for a real completion, which changes what the feature feels like to use, not just what it costs. Just size your expectations off your own traffic. Which brings me to the honest part. ## What semantic caching costs you to run ![Every request pays the semantic lookup toll](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/ux3cdjdufpwk8zivmwo1.png) Nobody puts this in the intro paragraph, so here it is. You pay an embedding call and a vector search on **every single request**, including the 60 to 80% that miss. Embeddings are cheap compared to a chat completion, so the money side is fine. The latency is the thing to watch, because you have just added a network round trip to the front of every request in your system, including all the ones the cache cannot help with. At [Keploy](https://keploy.io) I built a retrieval-augmented chatbot over their documentation using vector embeddings, and docs traffic is close to the best case for this technique. People ask the same twenty questions in fifty phrasings, forever. Even there, the honest framing is that you are trading a guaranteed small cost on 100% of requests against a large saving on a minority of them, and you need to actually measure that ratio before assuming it comes out ahead. Then there is staleness. Your cache does not know your prices changed on Tuesday. The stored answer is a frozen snapshot of what the model said, plus whatever context it was given at the time, and it keeps being served until its expiry time runs out. Short expiry times are safer and hit less. Long ones are the opposite. Pick deliberately. Multi-turn conversations are worse. A follow-up like "and what about the second one?" embeds to almost nothing useful, because the meaning lives in the previous four messages and not in that sentence. Most sane implementations just refuse to cache beyond a few turns of history, and that is the correct call. And you now operate a vector store. That is one more thing to size, monitor, and pay for. ## One cache, many tenants, and the leak nobody plans for This is the part that turns semantic caching from a performance feature into a security decision, and it is why I would not hand-roll one at the application layer in an enterprise setting. A semantic cache with one global namespace returns the nearest previous response across every user in it. Not the nearest response *belonging to that user*. The nearest one, period. So picture two customers of the same SaaS product asking structurally similar questions about their own account data. Their prompts embed within 0.93 cosine of each other, because they are the same question about different companies, and the cache cheerfully hands one customer the other's cached answer. Nothing errored. Nothing logged a violation. It looks exactly like a successful cache hit, which is the worst property a data leak can possibly have. ![A shared cache can cross tenant boundaries](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/bflbiben9y76am58s2yq.png) There is a subtler version too. Even with no wrong response served, cache hits are dramatically faster than misses, and that timing difference is observable from outside. Somebody probing your API can learn which questions have already been asked by other tenants just by watching time to first token. The fix is not clever, it is structural. The cache key has to be partitioned by a tenant identifier resolved from something you trust, meaning the API key, a virtual key, or a signed token claim. Never from the request body, because the request body belongs to the attacker. And the lookup has to be scoped to that namespace so a cross-tenant match is not merely unlikely, it is unreachable. If that sounds like something you would rather not rebuild inside every service you own, I agree with you, and that is the real argument for doing this at the gateway. ## When should you not use semantic caching? ![Some workloads should bypass semantic caching](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/bca2riv6xiz4clpbngq4.png) Some genuine "don't bother" cases, because this is not free and it is not universal. **Anything that must be current.** Live inventory, account balances, order status. A stale answer here is worse than a slow one. **Anything where the parameters matter more than the phrasing.** Analytical queries over dates, IDs, and entities are precisely where embeddings blur the thing you needed preserved. If you cannot pull those values out into the cache key, skip it. **Anything high-stakes and low-volume.** Medical, legal, financial advice. The negation research above is not a curiosity in those domains, it is a lawsuit. And if your volume is low, you were not saving much anyway. **Long open-ended conversations.** A 10 to 20% hit rate while adding latency to 100% of requests is a bad trade. Where it does earn its place: support bots, docs assistants, FAQ layers, onboarding flows, internal knowledge search, and any product where a large user base asks a small set of questions in a lot of different ways. That describes a very large share of enterprise AI traffic, which is why this matters at all. ## How Bifrost does semantic caching at the gateway I have been running [Bifrost](https://www.getmaxim.ai/bifrost) as my AI gateway for a while now, and its semantic cache is the cleanest implementation of everything above that I have read, mostly because I could actually read it. You can check out their [GitHub repository](https://github.com/maximhq/bifrost) as well. The design choice I like most is that it is **two layers, not one**. Every request first goes through a direct hash lookup. If the prompt is byte-identical to something already cached, it returns immediately with zero embedding overhead, which matters because you just skipped the round trip that would otherwise tax every request in the system. Only on a direct miss does it embed the query and run the similarity search. So the cheap path stays cheap, and the expensive path only runs when it might actually pay off. ![Direct and semantic cache layers at the gateway](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/j1gnm7fit9fqcewhgu4m.png) Then there is the thing that answers the multi-tenancy section directly. **Caching only activates when a request carries a cache key**, passed as an `x-bf-cache-key` header. There is no global-namespace mode for you to accidentally ship. If you want per-tenant isolation, the tenant identifier goes in that header, and cross-tenant matches then cannot happen at all, because those entries are not in the same partition. Making the safe thing mandatory instead of optional is a real design decision, and most implementations get it wrong. A few more of the knobs, since the defaults tell you what the authors actually believe: - `threshold` defaults to **0.8** for semantic hits, overridable per request with `x-bf-cache-threshold`. - `ttl`, the time to live on an entry, defaults to **5 minutes**. That is a deliberately conservative staleness stance, and you can override it per request too. - `conversation_history_threshold` defaults to **3**, which means it stops caching once a conversation runs past 3 messages. That is exactly the multi-turn failure I described earlier, handled by default. - `cache_by_model` and `cache_by_provider` are both on by default, so a cached GPT answer never gets served to a Claude request. - The vector store is pluggable across Redis or Valkey, Weaviate, Qdrant, and Pinecone, so you are not forced into adopting a new database. And every response carries a `cache_debug` block with `cache_hit`, `hit_type` (direct or semantic), the actual `similarity` score, and a `cache_id`. That last one is what makes invalidation possible, since you can delete a single poisoned entry by its ID, or clear an entire partition by cache key, straight over the API. If you have ever had to explain to a customer why the bot gave them the wrong answer twice, you will understand why having that similarity score visible per request is worth a lot. The cost story is the obvious one, and the numbers are the ones from earlier in this post rather than anything I can promise you. Every cache hit is a completion you did not pay for at all. What a gateway changes for enterprises is that the cache now sits next to budgets, virtual keys, and routing in one place, so the same layer deciding *which* provider gets a request is also deciding whether the request needs a provider at all. That routing side is a whole topic of its own, and I wrote it up separately in [what adaptive load balancing actually is](https://www.swapnoneel.site/blog/what-is-adaptive-load-balancing). Bifrost adds under 100 microseconds of overhead at 5,000 requests per second, which is a rounding error next to the embedding call, and honestly next to anything else in an LLM request path. ## What should you actually do first? Do not start by building the cache. Start by measuring how repetitive your traffic actually is. Take a week of your logs, embed the queries, and count how many of them land within 0.92 of an earlier one. That single number is your ceiling, and getting it takes an afternoon. I did a version of this on a contract a while back, building an internal tool that captured an AI product's logs and turned them into reports on latency and probable slowdowns (keeping it vague here, can't say much more than that lol). The thing I took away from it is that you learn more from one honest week of your own traffic than from every benchmark on the internet. If that number comes back at 30% or better, turn semantic caching on at your gateway, partition the cache key by tenant from day one, keep the expiry short, and watch the similarity scores on your hits for the first few weeks instead of trusting the threshold. If it comes back at 8%, you have found a much more interesting problem than caching, which is that your users are all asking different things. ![Measure repetition before turning caching on](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/wpivlvi3ri9qiqp79l0k.png) And if you have run a semantic cache in production and watched it serve something it absolutely should not have, please tell me about it in the comments, those stories are the useful ones. I write more about LLM infrastructure and building with AI over at [swapnoneel.site](https://www.swapnoneel.site), and I'm on [X (swapnoneel123)](https://x.com/swapnoneel123) if you feel like arguing about thresholds. ## Chaos Testing Explained: A Comprehensive Guide URL: https://www.swapnoneel.site/blog/chaos-testing-explained-a-comprehensive-guide Date: 2025-01-09T01:26:15.000Z Summary: Table of Contents Chaos testing, also known as chaos engineering, is one of the most-used methodology to test the resilience and reliability of systems, and is a key part of modern resilience testing practices. Originating from Netflix’s famous Chaos Monkey tool, chaos testing has become a key practice in building robust distributed systems. In this [...] Chaos testing, also known as chaos engineering, is one of the most-used methodology to test the resilience and reliability of systems, and is a key part of modern [resilience testing](https://keploy.io/blog/community/why-apps-crash-and-how-resilience-testing-can-help "resilience testing") practices. Originating from Netflix’s famous Chaos Monkey tool, chaos testing has become a key practice in building robust distributed systems. In this blog we’ll be diving into the realm of Chaos Testing and understand it in detail. So, let’s begin! ## **Why Chaos Testing is Crucial?** Nowadays, modern systems are increasingly distributed, running on cloud infrastructure and microservices architectures. While these designs offer scalability and flexibility, they also introduce complexities and potential failure points. This is where it ensure that systems can withstand real-world disruptions like server crashes, network partitions, or latency spikes, ultimately enhancing reliability and user trust. ## **Fundamentals of Chaos Testing** ### **What are the Core Principles?** 1. **Embrace Failure:** Assume that failures are inevitable and plan for them. 2. **Test in Production (With Safeguards):** Simulate real-world conditions to get accurate insights. 3. **Minimize Blast Radius:** Limit the scope of experiments to avoid widespread disruptions. ![Core principles of chaos engineering diagram](https://wp.keploy.io/wp-content/uploads/2025/01/What-are-the-Core-Principles.webp) ### **How it differs from other types of testing?** Unlike [load or functional testing](https://keploy.io/blog/community/all-about-load-testing-a-detailed-guide), chaos testing focuses on the system’s behavior under unexpected conditions rather than verifying its performance under normal operations. It’s less about finding bugs and more about uncovering systemic weaknesses. ### **What are the Key Goals of Chaos Testing?** - **Identify Weaknesses and** expose hidden vulnerabilities in the system. - **Validate Redundancy Mechanisms to e**nsure failover and backup systems function as expected. - **Improve MTTR and e**nhance the mean time to recovery after an incident. ## **The Chaos Testing Process** ![Step-by-step chaos testing process diagram](https://wp.keploy.io/wp-content/uploads/2025/01/The-Chaos-Testing-Process.webp) ### Step-by-Step Guide 1. **Define Steady-State Behavior:** Identify and quantify what a healthy system looks like. For example, steady-state metrics may include response time under specific traffic, throughput, and error rates. These metrics serve as a baseline to detect deviations during experiments. 2. **Hypothesize Potential Failure Points:** Collaborate with the team to brainstorm possible weak points in the system. Common examples include single points of failure, network bottlenecks, and third-party service dependencies. And, ask questions like: What happens if a critical service goes down? How will the system behave under high latency or packet loss? 3. **Inject Failures:** Use chaos testing tools to introduce controlled disruptions. Examples include: - Terminating a critical service instance (using tools like Chaos Monkey). - Simulating high network latency or packet drops (using tools like Gremlin). - Creating resource contention (e.g., high CPU or memory usage). - Testing infrastructure issues like DNS outages or unavailable storage volumes. 4. **Observe and Analyze Results:** Monitor system behavior and analyze logs and metrics to understand failure impact and identify areas of improvement, during the chaos test using observability tools (e.g., Grafana, Prometheus, or Datadog). The key areas to monitor, includes: - Latency: Did response times spike? - Error Rates: Were there any increased 4xx or 5xx errors? - Availability: Did the system maintain its service level objectives (SLOs)? 5. **Iterate and Improve:** Based on findings, implement changes to improve system resilience. This could include updating retry logic, adding redundancy, or improving failover mechanisms. And, retest after applying changes to validate improvements. ### Tools and Frameworks Popular tools for chaos testing include: - **Chaos Monkey:** Focuses on terminating instances in production environments. - **Gremlin:** Offers a broad set of failure injection scenarios. - **LitmusChaos:** Designed for Kubernetes environments. - **Chaos Toolkit:** An open-source platform for automating chaos experiments. - **PowerfulSeal:** Designed for cloud-native environments, particularly Kubernetes. ## What are the common pitfalls for Chaos Testing? 1. **The system can get overloaded because of** running large-scale experiments without proper safeguards. 2. Conducting chaos tests without defined goals and lack of clear objectives can lead to confusion and wasted efforts. 3. **Poor Communication and** failing to inform stakeholders about planned experiments can cause unnecessary panic. ## What are the Best Practices for Chaos Testing? 1. **Start Small by b**eginning with isolated experiments and gradually expand scope. 2. **Automate Chaos Experiments by** integrating tools and scripts into CI/CD pipelines. 3. **Collaborate Across Teams by** ensuring alignment between development, operations, and QA teams. ## Let’s understand the Case Studies ### Netflix Netflix pioneered chaos testing with Chaos Monkey, a groundbreaking tool that randomly terminates production instances to test system resilience. The tool’s inception marked the beginning of a suite of resilience tools known as the Simian Army, which includes: - **Latency Monkey:** Simulates network latency to test service timeouts and fallback mechanisms. - **Conformity Monkey:** Identifies instances that do not adhere to best practices and shuts them down. - **Chaos Gorilla:** Simulates entire availability zone outages to ensure regional failover. Netflix’s comprehensive approach to chaos engineering ensures continuous improvement in reliability across a complex, global infrastructure serving millions of users. ### Other Examples - **Twilio:** Twilio leveraged Gremlin to simulate various network disruptions, such as latency spikes and packet loss, to improve API reliability and enhance customer communication services. These tests revealed key improvements for retry logic and failover systems. - **Google:** Google’s DiRT (Disaster Recovery Testing) program goes beyond chaos testing by simulating massive-scale failures, such as data center outages, to validate global failover capabilities and disaster recovery plans. ## How Keploy Can Contribute to Chaos Testing? Keploy, primarily designed as an open-source testing platform for generating test cases and ensuring API reliability, can be a **part of a chaos** [**testing strategy**](https://keploy.io/blog/community/a-test-strategy-is-critical-for-your-project-success) when focusing on regression and behavior of API under failure scenarios. It can work alongside chaos testing tools to provide a more comprehensive resilience testing approach, especially in microservices architectures. **_But question is how?_** It can help as follows **–** - **Simulating API Failures**: [Keploy](https://keploy.io) can generate and simulate unexpected API behavior (e.g., failed responses, timeouts, or incorrect data) to test how the system handles such disruptions. This aligns with chaos testing’s goal of uncovering vulnerabilities. - **Validating System Behavior Under Stress**: By replaying API calls and testing against predefined baselines, Keploy can help ensure that the system continues to perform reliably when subjected to disruptions. - **Enhancing Observability**: It can provide insights into how APIs and microservices interact during failure scenarios, helping teams identify weak points and areas requiring redundancy. - **Automation and Integration**: By automating failure scenarios and integrating them into CI/CD pipelines, Keploy act as a step in a broader chaos testing framework. ## Conclusion Hence, by integrating chaos testing into their culture, organizations can continuously enhance robustness, improve recovery times, and maintain user trust in their systems. I hope, you were able to learn something new today, because that’s the wrap for now! If you have any more questions, you can drop it down in the comments. ## FAQs ### **How does chaos testing differ from traditional stress testing?** While stress testing examines how systems perform under extreme workloads, chaos testing focuses on unpredictable failures, such as service disruptions, network issues, or hardware failures. Chaos testing aims to uncover hidden vulnerabilities and improve resilience, even in normal workloads. ### **Can chaos testing be applied in a non-cloud environment?** Absolutely. Chaos testing is not limited to cloud-native systems. It can be used to test on-premise infrastructure, legacy systems, or hybrid environments by simulating failure scenarios like disk failures, power outages, or network disruptions. ### **What is the role of observability in chaos testing?** Observability tools like Grafana or Prometheus are crucial for monitoring system behavior during chaos experiments. They help capture real-time metrics, logs, and traces to analyze the impact of failures and verify if systems meet their reliability goals. ### **What are the ethical considerations of chaos testing in production?** Ethical chaos testing requires safeguards to minimize risks to users. Experiments must be carefully controlled, with limited blast radius, and conducted during low-traffic periods. Additionally, compliance with data privacy laws and transparency with stakeholders is essential. ### **How do you integrate chaos testing into CI/CD pipelines?** Chaos experiments can be automated and added to CI/CD workflows using tools like Gremlin or LitmusChaos. By running these tests during staging or pre-deployment, teams can ensure that new updates or configurations won’t compromise system resilience. ![Thank you graphic for chaos testing guide](https://wp.keploy.io/wp-content/uploads/2024/11/Thank-you.webp) ## Top 5 Best IDEs to use for Python in 2024 URL: https://www.swapnoneel.site/blog/best-ides-for-python-in-2024 Date: Fri, 25 Oct 2024 12:13:35 GMT Summary: PyCharm, VS Code, Spyder, Jupyter, and Thonny each fit a different Python workflow. Compare their strengths, limits, and the kind of work each makes easier. The best Python editor is the one that makes your next task easier. That may be a full IDE with refactoring and debugging, or it may be a small editor with a terminal beside it. So, which one should you install? It depends on what you are doing with Python. A data scientist opening a notebook has a different problem from a beginner tracing their first loop, and both have a different problem from someone maintaining a large web application. These five options fit those kinds of work in different ways, so pretending one tool wins every category would be misleading. My pick is at the end, but keep your machine, project size, and patience for configuration in the decision. ## PyCharm PyCharm is the easiest recommendation for a large Python project. It understands a project as more than a folder of files, so navigation, refactoring, debugging, virtual environments, and Git all live in one place. ![PyCharm Professional IDE interface](https://textdata.cn/images/blog/pycharm-professional.png) The editor can complete code, flag errors while you type, and rename symbols across a project. Its debugger is comfortable once you learn the controls, and the Professional edition adds support for web frameworks such as Django and Flask. The tradeoff is weight. PyCharm can use a lot of memory, especially on an older machine, and the Professional edition is paid. The free Community edition is enough for many Python projects, but check which features you need before building your workflow around one edition. Choose PyCharm when you want Python-specific tools ready when you open the project. I would not choose it for a quick one-file script unless I already had it open. ## VS Code VS Code is the most flexible option in this list. It is a general editor rather than a Python IDE out of the box, but the Python extension adds code completion, debugging, environment selection, and test support. ![VS Code Python development environment](https://external-preview.redd.it/Uz0PH-r8nGx8gU9UCHURirqrtXgLhtqJiNDVgT03jtw.jpg?auto=webp&s=580d0dc62b6581d2a8ff8e628d3bea5d53bdf5d2) The built-in terminal and Git view make it easy to move between editing, running a command, and checking a change. You can also add support for Docker, notebooks, JavaScript, and many other tools without leaving the editor. That flexibility has a price. New users can spend more time choosing extensions and settings than writing Python. When the wrong extension takes over formatting or the interpreter points at the wrong environment, the editor does not always make the cause obvious. Choose VS Code if you work across several languages or want one editor that can grow with your projects. It is my general recommendation, even though it takes more setup than PyCharm. ## Spyder Spyder is built around data exploration. Its layout gives you an editor, an interactive console, plots, and a variable explorer in the same workspace. You can run a few lines, inspect the resulting DataFrame, and keep going without adding print statements everywhere. ![Spyder scientific Python IDE](https://www.spyder-ide.org/assets/media/website_screenshot.png) That variable explorer is the reason to try Spyder. Seeing arrays and tables directly is useful when you are learning how a calculation changes the data. Spyder is free and open source, and it is often installed alongside Anaconda. It is less comfortable for web applications or a general software project with many packages and services. The project tools are not as broad as those in PyCharm or VS Code, so the editor can feel like the wrong shape once your work stops being notebook-like. Choose Spyder when your day is mostly NumPy, Pandas, plots, and experiments. ## Jupyter Notebook Jupyter Notebook is an interactive environment rather than a traditional IDE. You write code in cells, run one cell at a time, and place Markdown explanations beside the output. ![JupyterLab interface preview](https://jupyter.org/assets/homepage/labpreview.webp) That workflow is excellent for exploratory data analysis, machine learning experiments, and teaching. You can show the code that produced a chart directly next to the chart, which makes a notebook easy to share as a record of an investigation. The same flexibility can make a notebook messy. Cells can run out of order, hidden state can survive longer than you expect, and a project spread across several notebooks is harder to maintain than a normal Python package. A notebook is a poor place to hide application logic that needs regular tests. Choose Jupyter when you want to ask questions of data and see the answer immediately. Move reusable code into `.py` files once the experiment starts becoming a product. ## Thonny Thonny is aimed at people who are learning Python. The interface removes many distractions, and its debugger lets you step through a program while watching values change. ![Thonny IDE for beginner Python developers](https://thonny.org/img/screenshot.png) That visual step-through is useful because beginners often know what a line says but not when it runs or what value it leaves behind. Thonny makes those changes visible without asking you to configure a large toolchain first. It is free and friendly, but it is not designed for a large application. The extension and customization choices are limited, and you will probably outgrow it once you need a more complex project layout or a broad set of integrations. Choose Thonny if you are learning the language and want the editor to stay out of your way. ## How to choose without overthinking it Start with the work you will do most often. If you need a debugger and project-wide refactoring, try PyCharm or VS Code. If you inspect tables and plots, try Spyder or Jupyter. If you are learning your first loops and functions, Thonny is enough. Also consider the cost of a tool you will not use. A large IDE cannot fix unclear requirements, and a notebook cannot give a production service a test suite. Your editor should support the next problem you expect to solve, not the most impressive screenshot. ## My pick For a general Python workflow, I would pick VS Code because it handles Python well and leaves room for other languages and tools. PyCharm is the better choice when Python is the whole project and you want its deeper project support without assembling extensions. That is my winner for this list, but that's just me, and your workflow might be different. There are plenty of other editors worth trying. Give each option one real task instead of judging it from a feature page. The small annoyances show up when you create an environment, run a test, jump to a definition, and debug a failing line. For more posts, you can follow me on [Twitter (swapnoneel123)](http://twitter.com/swapnoneel123). My [GitHub (Swpn0neel)](https://github.com/Swpn0neel) has some of my projects too. ## Understanding Semantic Versioning URL: https://www.swapnoneel.site/blog/understanding-semantic-versioning Date: Sat, 01 Jun 2024 19:35:38 GMT Summary: Learn the basics of Semantic Versioning and how it helps developers manage software changes effectively... Often enough, we see version numbers like 1.0.2, 2.5.6 or something similar, associated with a software product. But, you may think what do these numbers actually represent, and why do we use them? We could've simply used numbers like 1, 2, 3, etc. for naming each versions! But, let me tell you, we use them because we’re following a best practice called Semantic Versioning. When we use Semantic Versioning, developers can easily predict whether a change will break their code or not. And also, the numbers give a clue to the kind of changes that have occurred. Now, let's dive deep, and understand each and everything about semantic versioning! ## What is Semantic Versioning? Semantic Versioning, sometimes abbreviated to SemVer, is a software versioning scheme using a three-part number format MAJOR.MINOR.PATCH, and this versioning scheme helps developers and users understand the nature of changes in the software. ![Semantic versioning MAJOR.MINOR.PATCH structure](https://cdn.hashnode.com/res/hashnode/image/upload/v1717267689583/94e58a9e-c8c5-4fbc-9a80-29081327b784.png) For the 2.6.8 version, for example: - 2 is its MAJOR version. - 6 is the MINOR version. - 8 is the PATCH version. Now, let's break down these 3 components and try to understand them individually: ### Patch Version Patch versions are generally used for bugfixes, and there are no functionality changes. You have heard about the term "hotfix", it's the patch version update that fixes any breaking changes! ![Patch version increment diagram](https://cdn.hashnode.com/res/hashnode/image/upload/v1717267567041/f64b3f07-0d5d-4c47-90d6-a5af7e6f262a.png) When you increase a new patch, you increase the rightmost number by 1. From 1, you increase it to 2, then to 3, and so on and to keep in mind, there are no limits to these numbers, so once you reach 9, you continue from 10, 11, 12 and so on. ### Minor Version The second number in the middle is called the minor version number. It is used when you release new functionality in your project. It could be a completely new feature or some added functionality to an already existing feature. ![Minor version increment diagram](https://cdn.hashnode.com/res/hashnode/image/upload/v1717267591031/fd410534-b0a2-46f0-abd8-2318835f5031.png) When you increase the minor version, you also increase it by one, and just like patch versions, it doesn't have any limits. But when you increase the minor version, you must reset the patch version to zero. ### Major Version The leftmost number is a major version. When you increase the major version, you tell people that there are backward-incompatible changes. People may experience breakage if they use the next version. It's often associated with a complete overhaul of the product. But, in some cases, it may also happen that we increment the major version, when a lot of new features along with a lot of improvements to the pre-existing features are launched at once! ![Major version breaking change diagram](https://cdn.hashnode.com/res/hashnode/image/upload/v1717267684973/accc1756-5772-478f-83dc-dcc5d7c7c6fa.png) When you increase the major version number, you have to reset both patch version and minor versions. ## Additional version types The above mentioned three version types are well-defined by some systematic rules, but for the upcoming version types or details that we are about to discuss doesn't have any proper rules and regulations and it varies between different products and organizations. ### Pre-release version If you want to create a pre-release version (like an alpha or beta version), you can add a hyphen `-`, followed by the words `alpha` or `beta`. There are no hard and fast rules for pre-releases, so you can name them anything you want. Usually, we use alpha or beta, followed by a number. For example, version `2.0.3-alpha2` is the second alpha release built on top of version `2.0.3` of the actual product. A pre-release version means the version is unstable and might not meet the intended final quality level. It often contains known and identified bugs, which are desired to be fixed, and even incomplete feature implementation. It's often released for closed/public testing to gather feedback or just to test the current implementation. ### Build Metadata Build metadata are specified by appending a plus sign and a series of dot-separated identifiers. For example, version 1.2.0+20130313144700. It provides details beyond the core version number, typically used for internal tracking purposes during development and testing. You can encode full commit id in the build metadata, so it's faster to analyze from what commit this software was actually build on, or you can encode the date/time of the particular build, machine it was build on. ## Initial Development Version `0.y.z` is for software in the initial development stage. Software using a 0 MAJOR version indicates that it is at an early development phase, and with any version, anything might get changed. During this period, the product is considered to be unfit for public usage, and is purely in the development stage. During this phase, we generally start our project with version `0.1.0` and when we reach a stable state and our product is almost feature complete for the launch, we generally test it in the `alpha` and `beta` release. After that, we increase the version to `1.0.0` upon release. ## Conclusion By now, I think you have realized that by providing clear rules and guidelines for incrementing version numbers, it helps developers communicate changes effectively, manage dependencies, and ensure predictable updates. Also, if you want to see an example of how semantic versioning is implemented in a real-world project, you can check out my latest project [Get Response](https://www.npmjs.com/package/get-response), which is a Node.js based command-line interface (CLI) tool that interacts with the Google's Gemini API to generate content based on the user input, and can also automate task for you. Hence, that's a wrap for now, and if you want to learn new things like this, you can follow me at [Swapnoneel Saha](https://hashnode.com/@Swapn0neel) and also follow me on [Twitter (swapnoneel123)](http://twitter.com/swapnoneel123) where I share more such content through my tweets and threads. I wish you a great day ahead and till then keep learning and keep exploring!! ![Thank you graphic for semantic versioning blog](https://cdn.hashnode.com/res/hashnode/image/upload/v1716652301849/8327a90c-373e-4837-9102-e67bb38def0c.png) ## Software Testing Pyramid URL: https://www.swapnoneel.site/blog/software-testing-pyramid Date: Mon, 29 Apr 2024 12:25:01 GMT Summary: The testing pyramid places many fast unit tests below fewer integration tests and a small number of end-to-end journeys. Use it as a feedback and maintenance guide, not a rigid law. Software testing asks a plain question: does the program behave the way you expect when someone uses it? A useful test gives you evidence before a change reaches a user, and a failing test gives you a smaller place to start looking. The testing pyramid is a way to divide that evidence. You put many fast tests at the bottom, fewer tests that check real connections in the middle, and a small number of full user journeys at the top. The shape is a reminder about feedback speed and maintenance cost, not a law that every project must follow exactly. ## The three layers ![Software testing pyramid architecture diagram](https://cdn.hashnode.com/res/hashnode/image/upload/v1714334533795/f1fd8287-3ef7-40d5-9c49-1f9b585f2235.png) Read the pyramid from the bottom upward. Each layer answers a different question, and each one catches failures that the others can miss. ### Unit tests check one piece of logic A unit test exercises a small part of your code, usually a function, method, or class, without starting a database or calling a remote service. You give the unit an input, make a prediction, and check the result. These tests are usually quick to run, which makes them useful while you are editing code and in continuous integration. They also make failures easier to inspect because fewer moving parts are involved. The tradeoff is isolation. A unit test can prove that a function handles a discount correctly, but it cannot prove that your checkout service sends the right value to the payment provider. ### Integration tests check connections Integration tests exercise the boundary between pieces of a system. That might be an HTTP handler talking to a service, a repository talking to a database, or two modules passing data between each other. The test setup is heavier because you need more than the function under test. You might start a test database, provide a configured client, or run a local service. That extra work makes these tests slower, but it also lets them catch mismatched fields, bad serialization, missing configuration, and other connection problems. You do not need to avoid every fake dependency here. The useful question is which boundary you want to check. If the database is the subject, use a real isolated database. If an external billing service is unavailable in tests, a controlled substitute may be the safer choice. ### End-to-end tests check a user journey End-to-end tests drive the application as a user would. A test might open the login page, submit credentials, and check that the account page appears. It crosses the browser, application server, data store, and other configured pieces. That realism comes with a price. These tests take longer, need more setup, and can fail because of a browser timing issue or an environment problem. Keep them for flows where a failure would matter to a user, then use the lower layers for the many smaller cases underneath. ## Why the shape matters Suppose a tax calculation is wrong. A unit test can point to the calculation within seconds. If the only test is an end-to-end checkout test, you first wait for the whole flow, then inspect several services before finding the same mistake. The pyramid pushes most feedback toward the cheaper layers. It also limits the amount of setup you have to maintain. A project with only end-to-end tests can work, but each small code change may require a long, fragile journey before you know whether the basic logic still works. My caveat is that the picture can make teams chase a ratio instead of useful coverage. A small service with a few carefully chosen integration tests may not look like a perfect pyramid, and that is fine if those tests protect the real risks. ## How to build the layers Start with the behavior that would hurt if it broke. Write unit tests for pure calculations and branching logic. Add integration tests around the boundaries where data changes shape or leaves your process. Add end-to-end coverage for the shortest set of journeys that represent the product's most important actions. Run the fast tests on every change. Run integration tests in an isolated environment often enough to catch broken connections before release. Run end-to-end tests as part of the delivery checks, but do not make every small assertion depend on a full browser session. When a test fails, fix the test or the product code rather than making the assertion weaker just to get a green build. Also, delete tests that duplicate a lower layer without checking anything new. Test code is still code, and stale test code can waste your time. ## Where teams get stuck The first obstacle is usually setup. A new test suite needs a runner, fixtures, test data, and a place to run safely. Start with one narrow path and make it repeatable before adding more cases. Maintenance is the next cost. Tests that know too much about implementation details break during harmless refactors. Assert the behavior a user or calling function depends on, not every internal step used to produce it. The final trap is treating speed as the only measure. Fast tests that never exercise a real boundary will not find a broken database query. Slow tests that cover every tiny branch will make feedback painful. The layers work when each one has a job. ## The practical takeaway Use the pyramid as a conversation about risk. Unit tests give you quick answers about local logic, integration tests check that neighboring parts speak the same language, and end-to-end tests confirm that important journeys work from the outside. There is no prize for drawing the most symmetrical pyramid. Put the tests where they can tell you something useful, keep the expensive journeys focused, and make the fast checks easy to run before you forget why the change was made. ![Thank you graphic for software testing pyramid blog](https://cdn.hashnode.com/res/hashnode/image/upload/v1714334190050/d203af69-a3d1-4e93-827b-1ecb696255b9.png) ## Access Control Testing: Principles, Vulnerabilities & Tools URL: https://www.swapnoneel.site/blog/access-control-testing-guide Date: 2024-12-30T00:02:56.000Z Summary: Access control testing checks whether a specific user can perform a specific action on a specific resource. Test ownership, roles, direct endpoints, uploads, and side effects. Authentication answers one question: "Who are you?" Access control, also called authorization, answers the next one: "What are you allowed to do?" That distinction matters every time an API returns a record, changes an account, or accepts an uploaded file. A request can come from a correctly signed-in user and still be forbidden. If the application checks only the login and forgets the permission check, the user may be able to read another person's data or call an admin-only endpoint. This guide walks through the rules behind access control, the failures worth testing, and a practical way to turn those checks into regression tests. You can use the examples with a browser, an API client such as [Postman](https://keploy.io/blog/community/my-journey-of-automating-test-cases "Postman"), or an automated test runner. ## What access control is checking Every protected request has at least three parts: a subject, an action, and a resource. The subject might be a user or a service. The action could be reading, editing, deleting, or uploading. The resource is the thing being touched, such as an invoice, project, or user profile. The server should make the permission decision from those parts. Do not trust a role, account ID, or permission flag that arrives only in the request body. A client can change it before the request reaches your application. The useful test question is simple: if I change the identity, role, or resource ID in this request, does the server still make the right decision? ## Principles that make permissions safer ### Least privilege Give each account only the permissions it needs for its job. A support user may need to view a ticket but not delete it. A customer may edit their own profile but not another customer's profile. Least privilege reduces the damage caused by a stolen account. It also makes tests easier to reason about because every allowed action has a clear reason. ### Separation of duties Some operations should require more than one person or role. For example, the account that prepares a payment should not be the only account that can approve it. This limits what one compromised account can do by itself. ### Roles and policies Role-based access control, or RBAC, groups permissions into roles such as `admin`, `manager`, and `user`. It is useful when the rules are stable. Attribute-based access control, or ABAC, makes the decision from details such as the user, resource owner, location, or request time. The name of the model matters less than the testable rule behind it. Write down which roles can perform which actions, then test both sides of every rule. ## Common access control failures ### Horizontal privilege escalation This happens when one user can access another user at the same permission level. A classic example is changing `/user/123` to `/user/124` and receiving someone else's profile. The application authenticated you, but it failed to check that you own resource `124`. ### Vertical privilege escalation This happens when a lower-privileged user can perform a higher-privileged action. A normal user should not be able to call an admin endpoint simply by discovering its URL or copying an admin request. ### Insecure direct object references An ID in a URL is not a permission check. IDs, filenames, and document keys are often useful clues during testing because changing one may expose another record. The server must verify access to the referenced object for every request. ### Excessive permissions Sometimes the application works exactly as coded, but the role has more access than it needs. Review the permission matrix as well as the implementation. A test that passes because a user can delete every record is not a success. ### Unsafe file uploads Upload endpoints need checks for file type, size, storage location, and later execution. Try permitted and forbidden extensions in a safe test environment, and verify that a rejected upload is not stored or served as executable content. ## A practical testing workflow Start with a small permission matrix. Put roles in one column, actions in another, and record the expected response for each combination. Include ownership in the matrix when a user should access only their own records. Then capture one valid request for each protected operation. Keep the request body, path parameter, query parameter, cookie, and authorization header visible in your test notes. The permission decision can depend on any of them. ### Test horizontal access Sign in as user A and create or identify a resource owned by user B. Replay the request with user A's credentials and user B's resource ID. The server should reject it or return a response that does not disclose the protected data. For example, compare requests for `https://example.com/user/123` and `https://example.com/user/124`. A `200` response is not automatically a vulnerability, but receiving user B's private fields is a clear failure. ### Test vertical access Use a low-privilege account to call an administrative operation such as `https://example.com/admin`. Test the route directly, then test the same action through alternate HTTP methods or content types if the application supports them. Do not stop after hiding an admin button in the UI. The API must enforce the rule too. ### Test the request, not just the page Browser tests can miss authorization bugs in background requests. Inspect the API calls made by the page and change one permission-relevant value at a time. Check the status code and the response body; an error status with sensitive data in the body is still a leak. ### Test uploads and exports Try a permitted file and then a forbidden extension such as `.php` instead of `.jpg`. Check where the file is stored and whether the resulting URL can be guessed. Apply the same care to export endpoints, because a download that is hidden from the UI may still be reachable directly. ## Manual and automated checks Manual testing is useful when you are discovering the permission model. A proxy such as Burp Suite or OWASP ZAP lets you edit requests and compare the server's decisions. Postman is handy for keeping a small set of role-specific requests, and tools such as [Cypress or Playwright](https://keploy.io/blog/community/playwright-vs-cypress-choosing-the-best-e2e-testing-framework "Cypress or Playwright") can exercise complete user flows. Static analysis can find suspicious patterns in source code, such as hardcoded credentials or routes with missing middleware. Dynamic testing checks the running application, which is where configuration and service boundaries often change the outcome. Interactive analysis combines runtime behavior with code-level information. Automation pays off after you have a known-good matrix. Save the expected result for every role and action, run it in CI, and keep a regression test for every authorization bug you fix. If your service is already handling real API traffic, [Keploy's API test generator](https://keploy.io/api-testing) can capture authenticated requests and replay them as tests. Review captured tokens and redact secrets before storing the tests. ## What to verify in a test result An authorization test should verify more than a status code. Check that: - the response status matches the policy; - the body contains no fields from the protected resource; - the server does not reveal a useful difference between an existing forbidden record and a missing record, when that distinction matters; and - the denied request does not create, update, delete, or upload anything as a side effect. Also test expired tokens, missing tokens, tokens issued for another audience, and a user whose role changed after the token was issued. Those cases often expose stale permission checks. ## Keeping access rules maintainable Centralize permission decisions where practical so that different endpoints do not slowly develop different interpretations of the same role. Log denied access with enough context to investigate, but do not put passwords, raw tokens, or private request bodies in the logs. Review permissions when a feature changes. A new endpoint, background job, export button, or service-to-service call can create a second path to the same data. The UI is only one path. One caveat from working with API tests: a large number of passing requests can create false confidence if every request uses an admin token. Keep fixtures for the least-privileged roles too. They are the ones that prove the boundary exists. ## Final checks Access control is a server-side decision about a specific subject, action, and resource. Test ownership changes, role changes, direct endpoint access, alternate request shapes, and side effects. Then keep those cases in the regression suite so the permission boundary does not disappear during the next refactor. ## FAQs ### **How does Keploy assist in access control testing?** **Keploy** is an open-source testing platform that automates test generation for APIs. It captures API calls during runtime and helps validate access control policies by replaying these calls in test scenarios. Keploy’s features can identify misconfigurations in access permissions or API endpoints. ### **What’s the difference between authentication and authorization?** Authentication verifies a user’s identity (e.g., logging in with a username and password). Authorization determines what actions or resources a user is permitted to access. ### **How can DevSecOps integrate access control testing?** DevSecOps practices integrate security testing, including access control validation, into CI/CD pipelines. Tools like Keploy, OWASP ZAP, and automated test scripts can continuously verify access permissions during development. ### **How do access control mechanisms evolve with microservices architecture?** Microservices often rely on decentralized components. Access control involves: - API gateways for centralized policy enforcement. - Service-level policies (e.g., ABAC for inter-service communication). - Tools like Open Policy Agent (OPA) for flexible policy management. ### **How important is user feedback in refining access control policies?** Gathering user feedback on denied permissions or excessive restrictions helps refine access control rules, ensuring a balance between security and usability. ## How AI code is Transforming the Future of Software Development URL: https://www.swapnoneel.site/blog/ai-code Date: 2024-11-29T02:05:01.000Z Summary: AI-assisted code can handle boilerplate, explanations, tests, and refactors, but it can also produce convincing mistakes. This guide shows where to use it and how to review the result. AI-assisted code is software written with help from a model that predicts text from a prompt or the code around it. Sometimes the model completes one line. Sometimes it drafts a function, a test, or a whole file. That sounds close to having another programmer beside you, but the comparison has a sharp limit. The model does not own the problem, know your product rules, or feel the consequences of a bad change. You still have to decide what the code should do and check whether it actually does it. ## What people mean by AI code There are two common meanings. The first is code generated by an AI system from a natural-language request. The second is ordinary code that an AI tool has explained, refactored, tested, or reviewed. Tools such as GitHub Copilot, ChatGPT, and TabNine can help with both. A code editor assistant can suggest the next few lines while you type. A conversational tool can inspect a pasted function, explain an error, or propose a design. The interface changes the workflow, but the basic risk stays the same: a plausible answer can still be wrong. ![AI tools for solving coding problems](https://wp.keploy.io/wp-content/uploads/2024/11/AI-For-Solving-Coding-Problems.webp) ## Where AI helps with code ### Drafting repetitive code Ask for a small function with clear inputs and outputs, and an assistant can produce a first draft quickly. It is particularly useful for boilerplate such as serializers, configuration objects, command-line parsers, and test scaffolding. The useful phrase here is "first draft." Read the result before you keep it. Check the error path, the types, and the assumptions the prompt left unstated. ### Explaining unfamiliar code When you inherit a large function, ask the tool to describe its inputs, side effects, and failure cases. You can then ask about one branch at a time. This is often more useful than asking for a full rewrite because the explanation gives you a chance to notice a wrong assumption. ### Finding bugs and security mistakes An assistant can point out an unhandled `None`, a missing boundary check, or a query that builds SQL from raw input. Static-analysis products such as DeepCode and Snyk also use machine learning to find patterns associated with bugs and security problems. Treat those findings as leads. A model may warn about code that is safe in your context, and it may miss a problem that depends on configuration or data. Run a test and inspect the surrounding code before changing anything. ### Refactoring AI is handy for mechanical changes: renaming a variable across a file, converting a repeated block into a helper, or moving a function while keeping imports consistent. Give it a narrow change and a clear constraint. Broad requests such as "make this better" usually produce code that looks different without solving a specific problem. ### Writing tests An assistant can draft a test from a function signature. You still need to supply the behavior that matters. Ask for the normal case, a boundary case, invalid input, and the expected side effect. Then read the assertions closely. A test that only checks that a function returns a value can pass while the function is completely wrong. Tools such as Keploy take a different route by capturing real application requests and responses, then turning those interactions into tests. That can give you cases that a prompt would not guess, especially around API behavior. ### Teaching and learning If you are learning Python, JavaScript, or another language, ask for a small example and then change one part of it yourself. Ask why the change affects the output. This turns the assistant into a patient explanation tool instead of a copy machine. Do not skip the uncomfortable part of learning. If you accept every answer without tracing it, you may finish a task while learning almost nothing about the language. ## What you gain and what you do not The obvious gain is time on repetitive work. You can get past an empty file, generate a rough test, or translate a small pattern between languages without searching through several examples. You also get a second way to phrase a problem. That is useful when you know the outcome you want but cannot yet see the implementation. The model may suggest an approach you can evaluate, even when you do not use its code. But faster typing is not the same as faster delivery. If the generated code adds a hidden bug, you have moved time from writing to debugging. That trade can be worth it for a small helper and awful for authentication, billing, permissions, or data migration code. My own rule is deliberately boring: I will accept a generated snippet only when I can explain why it works and point to a test that would fail if it did not. That rules out a lot of impressive-looking output, and I am fine with that. ## Risks you need to handle ### Incorrect but convincing output Language models predict likely code; they do not run your application in their head. They can invent an API, use an outdated method name, or quietly change a requirement. Compilation catches some mistakes. It does not catch a wrong business rule. ### Missing project context The assistant may not know your database constraints, coding conventions, deployment environment, or the reason a strange-looking line exists. Include the smallest useful context, and never paste secrets, private keys, customer data, or tokens into a tool that your project has not approved. ### Security and licensing questions Generated code can contain an insecure default or a dependency you did not plan to maintain. Review new packages and run your normal security checks. Your organization may also have rules about sending source code to external services or accepting generated material, so check those rules before adopting the tool. ### Losing the ability to review If an assistant writes code faster than you can read it, the workflow has become unsafe. Keep changes small. Ask for a diff or one function at a time. Run tests after each meaningful change rather than collecting a large pile of suggestions and hoping the final build tells the truth. ## A safer way to use an AI coding assistant Start with a written requirement. Include the input, output, constraints, and one example. Ask for a plan before asking for code when the task is larger than a single function. Then review the result in this order: - Does it solve the stated problem rather than a nearby one? - What happens with empty, invalid, or unusually large input? - Does it change state, call a service, or expose data in a way you did not request? - Can you test the important behavior? Keep the generated change behind the same review, linting, type checks, and tests as any other change. AI code does not get a special path through the repository. ## Where this is heading The most useful assistants will get better at working with the files, tests, and tools that already define a project. That could reduce the amount of context you need to repeat in every prompt. It will not remove the need for judgment. A tool can propose a fix for a failing test, but you still decide whether the test describes the right behavior. It can explain a legacy module, but you still decide whether a rewrite is safe. The person responsible for the result remains the person who merges it. ## The short version AI code is useful when it removes typing and helps you think. It is risky when it replaces understanding. Use it for drafts, explanations, mechanical refactors, and test ideas. Keep the specification, review, and final decision with you. There is no shame in rejecting a suggestion that looks clever but cannot be verified. That is usually the more professional choice. ## FAQ’s ### Can AI generate test cases for my application? Yes! AI can automate the generation of test cases by analyzing your codebase and identifying potential scenarios to test. Tools like **Keploy** take this a step further by automatically capturing real-world scenarios as test cases during runtime. This ensures comprehensive coverage and helps simulate realistic conditions for robust software testing. ### How does AI handle legacy codebases? AI tools can assist in understanding, modernizing, and optimizing legacy codebases by analyzing patterns, detecting outdated code, and even suggesting updates to make them compatible with modern standards. Some advanced tools can also generate tests for legacy code to ensure reliability during refactoring. ### Is AI-generated code reliable for production use? AI-generated code is a great starting point, but it still requires human review to ensure reliability, security, and adherence to project-specific standards. Platforms like **Keploy** help improve reliability by generating tests to validate the AI-generated code in various scenarios. ### Will AI replace the need for human QA engineers? No. While AI can automate repetitive testing tasks and generate test cases, human QA engineers bring critical thinking, creativity, and domain expertise to ensure the software meets user expectations. AI acts as an augmentation tool, not a replacement. ### How do AI tools like Keploy integrate with existing workflows? Keploy and similar tools integrate effortlessly into CI/CD pipelines, capturing test cases during normal development and deployment processes. This ensures minimal disruption to your workflow while enhancing the quality of your software. ![Thank you graphic for AI coding blog](https://wp.keploy.io/wp-content/uploads/2024/11/Thank-you.webp) ## Comparing GitHub Copilot vs. ChatGPT for Unit Testing URL: https://www.swapnoneel.site/blog/comparing-github-copilot-vs-chatgpt-for-unit-testing Date: 2024-12-06T00:13:24.000Z Summary: GitHub Copilot works beside your code, while ChatGPT is better for conversation and reasoning around a test. Compare both on a real unit-testing task and see where each falls short. ChatGPT and GitHub Copilot can both write a unit test. That does not make them interchangeable. The difference shows up when the test needs context. Copilot works beside the file you are editing and can suggest a test from nearby code. ChatGPT gives you a conversation, which is better when you need to explain a failure, compare approaches, or reason through a missing case. I would not ask either tool to "write comprehensive tests" and paste the answer into a repository. A test can be syntactically correct, run green, and still assert almost nothing. The interesting comparison is how much work each tool leaves you before the test describes the behavior you actually care about. ## The two tools in plain English ChatGPT is a general conversational model. You provide code, an error, a requirement, or a question, and it responds with an explanation or a possible implementation. It is useful when you want to keep asking "why?" until the behavior makes sense. OpenAI releases newer models over time. The [impact of GPT-o3-mini on tech](https://keploy.io/blog/community/impact-of-gpt-03-mini-on-tech) is one example of how quickly the available options change, so treat a model name as a detail of the setup rather than the whole workflow. GitHub Copilot is an editor assistant. It uses the file around your cursor and other available project context to suggest code, complete a line, or draft a function. You stay in the editor, which makes it quick for small changes and repetitive test setup. ![ChatGPT Plus vs GitHub Copilot comparison](https://cdn.mos.cms.futurecdn.net/9HNs2rcSFyJepccD2sx2uk.jpg) ## A unit-testing task worth comparing Suppose you have a function that calculates a shipping charge. The happy path is easy: pass a valid order and check the amount. The useful tests are the ones around it: an empty order, an invalid address, a free-shipping threshold, and a service failure. Copilot may suggest the test file and infer the imports from the repository. That saves typing. It may also copy the implementation's assumptions so closely that the tests all repeat the same mistake. ChatGPT may give you a longer list of cases and explain why each one matters. You have to paste the relevant code and requirements, though, and the answer can drift away from your project's test framework or fixtures. That tradeoff comes up again and again: Copilot is close to the code, while ChatGPT is better at a back-and-forth discussion. ## Where ChatGPT is stronger ChatGPT is the better partner when the requirement is still fuzzy. You can describe the business rule, show a failing assertion, and ask it to separate the observable behavior from the implementation details. It is also useful for debugging a test that fails for a reason you do not understand. Ask it to trace the inputs, expected output, mocks, and side effects in order. The explanation is often more valuable than the replacement code. You can use the same conversation for test naming, fixture design, documentation, or an alternative implementation. That range is convenient when the problem is larger than one line in one file. The cost is context management. ChatGPT cannot safely infer your whole repository from a small pasted snippet. If you omit a fixture, environment variable, or dependency version, the answer may be polished and irrelevant. ## Where Copilot is stronger Copilot wins when you already know the test you want and need to write it. It can follow the existing imports, naming conventions, and nearby patterns. That makes it good at table-driven tests, mock setup, and the next case in a test file. It also keeps the feedback loop short. You write the assertion, inspect the suggestion, run the test, and correct it in the same place. For repetitive work, that is a real advantage. The weak spot is explanation. Copilot can produce a convincing test without telling you which requirement the test covers or which important case is missing. It may also generate generic assertions because the code around the cursor does not contain the product context. ## What both tools get wrong Neither tool knows whether your test is worth having unless you give it the behavior. Both can guess the wrong return value, mock the wrong boundary, or test a private helper instead of the public behavior that users depend on. They can also create brittle tests. A test that checks the exact order of internal calls may fail during a harmless refactor, while a test that checks only that no exception was raised may miss a broken result. Review generated tests for four things: the input that triggers the behavior, the output that proves it, the side effects that must not happen, and the failure path. If you cannot state what bug the test would catch, keep working on the test before asking a tool for more of them. ## Using them together There is a sensible combined workflow. Use ChatGPT to turn a requirement into a list of observable cases and to explain a tricky failure. Use Copilot to place those cases into the repository's existing test structure. Then run the tests yourself and delete anything that does not protect behavior. The two tools can speed up separate parts of the work, but neither one should decide that a green test suite means the feature is correct. ## Other tools worth considering Cursor IDE combines an editor with AI-assisted completion and refactoring. That may appeal to you if you want the conversation closer to the codebase. CodeAnt AI focuses more on code quality, best-practice checks, and security analysis. It belongs in a review workflow rather than being treated as a replacement for a unit-test design. For API behavior, a capture-based tool such as Keploy can fill a gap that code assistants often miss. Instead of guessing requests from a function, it records real application interactions and replays them as tests. That is a different job from drafting a unit test, but it can protect boundaries between services. ## My pick for unit testing If I am learning a codebase or trying to understand why a test should exist, I pick ChatGPT. If I already know the case and want to write it inside a familiar test file, I pick Copilot. For a team choosing one tool specifically for unit-test authoring, Copilot gets my vote because the editor context removes copy-and-paste work. ChatGPT is the one I would keep nearby for reasoning and debugging, so the strongest setup is often both when your budget and data policy allow it. But that's just me, and your workflow might be different. Start with one small feature, inspect every generated assertion, and keep only the tests that would catch a real regression. ## A note on generated test coverage Coverage numbers can rise while confidence stays flat. A generated test suite may execute many lines without checking the decision that matters to a user. Keploy's [unit test generator](https://keploy.io/blog/technology/revolutionising-unit-test-generation-with-llms) takes a code-semantic approach to drafting cases. It can reduce the manual setup, but you still need to review the resulting tests and remove cases that do not match the contract of your code. The hard part of unit testing is not producing more files. It is choosing assertions that make a future failure obvious. ## FAQs ### **Can I use GitHub Copilot and ChatGPT together to improve my coding productivity?** Yes, many developers find that using both tools together enhances their productivity. For example, you can use GitHub Copilot to generate code quickly within your IDE, while relying on ChatGPT for deeper explanations, debugging, and exploring alternative approaches. Combining both tools allows for a well-rounded coding experience that covers quick implementations and detailed context. ### **Does ChatGPT support collaboration within development teams?** While ChatGPT itself does not provide built-in team collaboration features, it can help your team improve by offering code reviews, architecture discussions, and exploring best practices through interactive conversations. For collaborative workflows and testing, tools like Keploy can enhance team productivity, especially by ensuring the reliability of APIs and minimizing regression issues. ### **Can ChatGPT or Copilot help with API testing similar to what Keploy offers?** ChatGPT and Copilot can assist in generating code for API tests, but they do not offer the specialized capabilities of Keploy, such as automated test generation, mocking, and seamless regression testing. Keploy focuses specifically on API reliability and robustness, making it a specialized choice for comprehensive API testing compared to the broader code generation capabilities of Copilot and ChatGPT. ### **How do GitHub Copilot and ChatGPT handle code security and sensitive data?** Both tools require careful usage around sensitive data. GitHub Copilot and ChatGPT are trained on large datasets, and they can sometimes make insecure code suggestions. Tools like Keploy, when integrated with your development process, can further ensure API testing covers potential vulnerabilities and edge cases to improve overall software quality. ## Designing Machine Learning Workflows in Python URL: https://www.swapnoneel.site/blog/designing-machine-learning-workflows-in-python Date: 2024-02-02T16:07:14.579Z Summary: A machine-learning workflow turns data into features, trains and evaluates a model, and saves the result. Build that process in Python with Fashion MNIST and IMDb examples. Machine learning code is rarely just a model. You also need data that the model can read, a repeatable way to turn that data into features, an evaluation that matches the problem, and a way to save the result for later. That collection of steps is a machine learning workflow. Python is useful here because the same language can handle data loading with Pandas, numerical work with NumPy, classical models with scikit-learn, and neural networks with TensorFlow or PyTorch. The order matters. If you clean the test data using information from the training data, your score becomes too optimistic. If you choose a metric that does not match the cost of an error, a high score may hide a bad product decision. ## Start with the question Before opening a notebook, write down what the model should predict and what a useful prediction would change. Predicting whether a review is positive is a classification problem. Predicting the price of a house is a regression problem. Grouping similar customers without labels is a clustering problem. This decision affects the target column, the model family, and the metric. It also tells you what kind of error matters. In a fraud system, missing a suspicious transaction may matter more than reviewing an extra legitimate one. In a delivery estimate, the size of the error may matter more than whether the answer is on one side of a threshold. ## Build the workflow in order ### Prepare the data The model cannot learn from a messy table in the same way a person can. You need to decide which rows are usable, which column is the target, how missing values are handled, and how categories or text become numbers. The test set must remain untouched while you make those choices. It is meant to imitate data the model has never seen. This example shows the shape of a classification workflow. It assumes `data.csv` contains numeric feature columns and a column named `target`. ```python import numpy as np import pandas as pd from sklearn.model_selection import train_test_split from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score, precision_score, recall_score dataset = pd.read_csv("data.csv") target_column = "target" # Remove rows that cannot be used for the target decision. dataset = dataset.dropna(subset=[target_column]) X = dataset.drop(columns=[target_column]) y = dataset[target_column] # Keep the test set separate until the final evaluation. X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y, ) # Fit preprocessing only on the training data by putting it in a pipeline. model = make_pipeline( StandardScaler(), LogisticRegression(max_iter=1000), ) model.fit(X_train, y_train) y_pred = model.predict(X_test) print("Accuracy:", accuracy_score(y_test, y_pred)) print("Precision:", precision_score(y_test, y_pred, zero_division=0)) print("Recall:", recall_score(y_test, y_pred, zero_division=0)) ``` The file and column names are assumptions, so change them for your dataset. The important part is the boundary: split first, fit transformations on the training data, and use the test data only for the final check. ### Create useful features A feature is an input the model can use. Raw data is not always arranged in a useful form. A timestamp may become hour and weekday columns. A sentence may become word features. A table with hundreds of related numeric columns may need dimensionality reduction. Principal component analysis, or PCA, is one way to reduce numeric features to a smaller representation. Fit it on the training data, then apply the already-fitted transformation to the test data. ```python from sklearn.decomposition import PCA pca = PCA(n_components=2, random_state=42) X_train_pca = pca.fit_transform(X_train) X_test_pca = pca.transform(X_test) ``` PCA is not automatically a good feature choice. Two components are easy to plot, but they may discard information that the model needs. Compare the reduced version with the original features rather than assuming fewer columns means a better model. ### Choose and compare models Model selection is a comparison against the problem, not a contest for the most complicated algorithm. A linear model can be easier to inspect and may work well when the relationship is simple. A tree-based model can capture different kinds of boundaries but may need its own tuning. Cross-validation gives you several training and validation splits instead of trusting one lucky split. Use the same scoring rule for each candidate. ```python from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import cross_val_score logistic_model = LogisticRegression(max_iter=1000) forest_model = RandomForestClassifier(random_state=42) logistic_scores = cross_val_score( logistic_model, X_train_pca, y_train, cv=5, scoring="accuracy", ) forest_scores = cross_val_score( forest_model, X_train_pca, y_train, cv=5, scoring="accuracy", ) best_model = ( forest_model if np.mean(forest_scores) > np.mean(logistic_scores) else logistic_model ) print("Logistic regression:", np.mean(logistic_scores)) print("Random forest:", np.mean(forest_scores)) ``` The comparison above uses the PCA features from the previous step. In a real project, put PCA inside a scikit-learn pipeline before cross-validation so each fold learns its own transformation. That prevents information from the validation fold leaking into training. ### Train and evaluate Training is where the model learns parameters from the training set. Evaluation asks how well those learned parameters work on data held back from that process. ```python best_model.fit(X_train_pca, y_train) y_pred = best_model.predict(X_test_pca) accuracy = accuracy_score(y_test, y_pred) precision = precision_score(y_test, y_pred, zero_division=0) recall = recall_score(y_test, y_pred, zero_division=0) print("Accuracy:", accuracy) print("Precision:", precision) print("Recall:", recall) ``` Accuracy is the fraction of correct predictions. Precision asks how many predicted positives were actually positive. Recall asks how many real positives the model found. Choose the metric before looking for the best score, otherwise it is easy to optimize for a number that does not describe the product. ### Save the model Training can be expensive, and a deployed application needs the learned model without retraining it for every request. Save the fitted model together with every preprocessing step it needs. ```python import joblib joblib.dump(best_model, "model.pkl") ``` If the feature transformation is separate from the model, save that transformation too. A production prediction must receive data in the same shape and scale used during training. ## Example with Fashion MNIST Fashion MNIST is a useful small example because every image has the same shape and the dataset already comes with training and test splits. The model below classifies the images into ten categories. ```python import numpy as np import tensorflow as tf from tensorflow import keras fashion_mnist = keras.datasets.fashion_mnist (X_train, y_train), (X_test, y_test) = fashion_mnist.load_data() # Pixel values arrive as integers from 0 to 255. X_train = X_train.astype("float32") / 255.0 X_test = X_test.astype("float32") / 255.0 model = keras.Sequential([ keras.Input(shape=(28, 28)), keras.layers.Flatten(), keras.layers.Dense(128, activation="relu"), keras.layers.Dense(10, activation="softmax"), ]) model.compile( optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"], ) model.fit(X_train, y_train, epochs=10, validation_split=0.1) test_loss, test_accuracy = model.evaluate(X_test, y_test, verbose=0) print(f"Test loss: {test_loss:.4f}") print(f"Test accuracy: {test_accuracy:.4f}") model.save("fashion_mnist_model.keras") ``` The dataset is loaded with the [keras.datasets.fashion](http://keras.datasets.fashion)\_mnist module. The images are stored as 28-by-28 pixel arrays, and dividing by 255 puts each pixel into a small numeric range. The `Flatten` layer turns each image into one vector. The hidden dense layer learns combinations of those pixel values, and the final layer produces ten class scores. `sparse_categorical_crossentropy` fits integer labels such as `0` through `9` without requiring you to convert them into one-hot arrays. The model trains with [model.fit](http://model.fit)() on the training images and checks the test images only after training. When the code is run through the terminal, the model is trained, evaluated, and the test loss and accuracy are printed, as shown below: ![Terminal output of Fashion MNIST model training and evaluation](https://cdn.hashnode.com/res/hashnode/image/upload/v1706888496190/80a5fff8-8809-43d7-b8cc-8459949edd5c.png) ## Example with IMDb sentiment Text needs a different feature step. A logistic regression model cannot read raw sentences, so `TfidfVectorizer` turns words into numbers based on how often they appear in a review and how unusual they are across the dataset. ```python import joblib import pandas as pd from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score from sklearn.model_selection import train_test_split df = pd.read_csv("imdb_reviews.csv") X = df["review"] y = df["sentiment"] X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y, ) vectorizer = TfidfVectorizer() X_train_vectors = vectorizer.fit_transform(X_train) X_test_vectors = vectorizer.transform(X_test) model = LogisticRegression(max_iter=1000) model.fit(X_train_vectors, y_train) y_pred = model.predict(X_test_vectors) accuracy = accuracy_score(y_test, y_pred) print(f"Accuracy: {accuracy:.4f}") joblib.dump(model, "sentiment_analysis_model.pkl") joblib.dump(vectorizer, "vectorizer.pkl") ``` The dataset is loaded with [pd.read](http://pd.read)\_csv() into a DataFrame. In this example, `review` contains the text and `sentiment` contains the label. The split happens before fitting the vectorizer, which keeps vocabulary information from the test set out of training. The [model.fit](http://model.fit)() call learns the relationship between the training reviews and their labels. The `model.predict()` call then produces labels for reviews it did not see during training. Finally, `joblib.dump()` saves both the classifier and the vectorizer because the deployed application needs to transform new text in the same way. The Keras example uses [model.save](http://model.save)() for the same reason. When the code is run through the terminal, the model is trained, evaluated, and the accuracy is printed, as shown below: ![Terminal output of sentiment analysis model training and accuracy](https://cdn.hashnode.com/res/hashnode/image/upload/v1706889095784/94b6cb10-93a0-4b56-8e9d-0cd704d94764.png) ## Moving from a notebook to an application Keep a record of the data columns, preprocessing steps, model version, and evaluation split. A saved model without the code that prepared its inputs is hard to trust and harder to reproduce. Before deployment, test the prediction path with new examples and invalid input. Watch for missing columns, unexpected categories, empty text, and values outside the range used during training. Log model inputs carefully and avoid storing private data unless you have a clear reason to do so. Once the model is running, monitor the inputs and the outcomes you can observe. Data changes over time, and a score from last month cannot tell you whether today's requests still look like the training data. ## What to remember A machine learning workflow is a chain. Start with a clearly defined prediction, prepare the data without leaking the test set, create features that represent the problem, compare models with a meaningful metric, evaluate on held-out data, and save every piece needed for prediction. The code is only one part of the job. The decisions around the code determine whether the final number tells you anything useful. I still find the data split and metric choice easier to get wrong than the model import, which is probably why they deserve more attention than the flashy part. For more posts, you can follow me on [Twitter (swapnoneel123)](http://twitter.com/swapnoneel123). You can also check my [GitHub (Swpn0neel)](https://github.com/Swpn0neel) for projects. ![Machine learning model evaluation matrix and performance metrics](https://cdn.hashnode.com/res/hashnode/image/upload/v1706889699251/e0331511-ab42-4e0c-997c-5cbe529b3888.png) ## Functional Testing: An in-depth overview URL: https://www.swapnoneel.site/blog/functional-testing-an-in-depth-overview Date: 2024-11-05T00:05:40.000Z Summary: Functional testing checks whether an application does what its requirements say. Learn a practical workflow, common types, tool choices, and the habits that keep tests useful. Functional testing asks whether a feature gives the right result when you use it. You provide an input, perform an action, and compare what the application does with what the requirements say it should do. For a login form, that means checking a valid login, a wrong password, a missing field, and an account that should not be allowed in. It does not primarily ask how many requests the server can handle or how quickly the page loads. Those are non-functional concerns. ## What functional testing checks Functional testing verifies behavior from the outside. It can inspect inputs and outputs without knowing how the code is written, which is why it is often a [black box testing](https://keploy.io/blog/community/black-box-testing-and-white-box-testing-a-complete-guide) technique. ![Functional testing overview diagram](https://wp.keploy.io/wp-content/uploads/2024/11/ChatGPT-Image-Feb-17-2026-06_40_00-PM-1024x683.webp) Imagine the feature as a small machine. You put something in, the machine performs its rules, and you check what comes out. You do not need to see the gears to notice that a valid password opens the account while an invalid password does not. That does not mean the internal code is irrelevant. A tester may use implementation details to choose better cases, but the final check should describe behavior a user or another service can observe. ## Why it matters A feature can look correct in a code review and still fail at its boundaries. A checkout may calculate the normal total correctly but mishandle an empty cart. An API may return the right record for one user but expose another user's record when an ID changes. Functional tests give you a repeatable way to catch those mistakes. They also make a requirement concrete: instead of saying "the form should work," you record what happens for a valid value, an invalid value, and a missing value. One caveat: a large functional suite is not automatically a good suite. If every test follows the happy path, the green build can still hide the bug you are most likely to ship. ## A functional testing workflow ### Read the requirement first Start with the user story, acceptance criteria, API contract, or other description of expected behavior. Write down the inputs, the result, and any state change. If the requirement is vague, ask for an example before writing the test. For a login feature, your notes might say: a valid account receives access; an invalid password receives an error; a missing password does not send a login request. Those statements are easier to test than a general sentence about authentication. ### Write the test cases Each test case should identify the feature, any precondition, the action, and the expected result. Give the case a name that tells you what failed, such as `rejects_login_when_password_is_missing`. Include boundary cases and failure paths. For a file upload, test the allowed type, an oversized file, a forbidden type, and an interrupted upload. You do not need to invent every possible input, but you should test the decisions the feature makes. ### Prepare a representative environment Run the test in an environment with the same important settings as the application you plan to ship. That includes the database shape, feature flags, authentication setup, external-service stubs, and test data. The closer the environment is to reality, the more useful a passing result becomes. At the same time, keep test data controlled so that one test does not silently change what another test expects. ### Execute and compare Run each case manually or with an automated tool. Compare the actual status, response, screen, stored data, and side effects with the expected result. A page that displays the right message but still creates a record is not behaving correctly. ### Record defects clearly When a case fails, save the steps, input, expected result, actual result, environment, and any useful logs. A short title such as "wrong account returned after changing user ID" is easier to act on than "profile bug." ### Retest and run regression checks After the defect is fixed, rerun the failed case. Then run the related suite because a change to one feature can affect another. Keep a regression test for important bugs so the same behavior does not disappear during a later refactor. ## Types of functional testing The names below describe where the test sits or what it covers. They are not completely separate activities. 1. [Unit testing](https://keploy.io/blog/community/what-is-unit-testing/ "Unit Testing") checks a small function or component in isolation. Developers often run it close to the code they are changing. 2. [Integration testing](http://https://keploy.io/blog/community/integration-testing-a-comprehensive-guide "Integration Testing") checks whether two or more parts work together, such as an API and its database. 3. [System testing](http://https://keploy.io/blog/community/all-about-system-integration-testing-in-software-testing "System Testing") checks the complete application against its functional requirements. 4. [User acceptance testing](http://https://keploy.io/blog/community/what-is-user-acceptance-testing "User Acceptance Testing (UAT)") lets a client or representative user check whether the product supports the intended work before release. 5. [Smoke testing](http://https://keploy.io/blog/community/developers-guide-to-smoke-testing-ensuring-basic-functionality "Smoke Testing") is a short set of checks for the main paths, such as opening the application, signing in, and creating a basic record. It tells you whether deeper testing is worth starting. You can apply functional checks at each level. A unit test for a tax function and a system test for checkout are both functional tests, but they answer different questions. ## Manual and automated testing Manual testing is useful when you are exploring a new feature or judging something that is difficult to express as an assertion. A person can notice confusing wording, an awkward flow, or a visual problem that a script will not understand by itself. Automation is better for repeatable checks. It can run the same login, purchase, or API request after every change and report exactly where the result changed. Tools such as Selenium, [Keploy](https://keploy.io/ "Keploy"), Appium, and Cucumber support different parts of this work. Automation does require maintenance. Selectors change, test data expires, and product rules move. Keep the tests close to the behavior they protect, remove cases that no longer describe a real requirement, and fix failures instead of marking them as ignored forever. ## Tools and where they fit Selenium automates browser interactions, so it is useful for web application flows that a user completes through a browser. ![Selenium web automation platform logo](https://wp.keploy.io/wp-content/uploads/2024/11/selenium-2-1024x304.webp) Keploy can generate functional and regression cases from real application interactions. It captures requests and responses and turns those interactions into tests, which can help when manually describing every API case would take too long. ![Keploy test automation platform overview](https://wp.keploy.io/wp-content/uploads/2024/11/keploy_coverimg-1024x615.webp) Appium is used for mobile applications on Android and iOS. It supports native, hybrid, and [mobile web applications](https://keploy.io/blog/community/essential-functional-testing-tools-for-mobile-development "mobile web applications"), so the same testing idea can cover more than a desktop browser. ![Appium mobile testing framework logo](https://wp.keploy.io/wp-content/uploads/2024/11/appium_coverimg-1024x394.webp) Pick the tool from the boundary you need to test. A browser driver is not the best answer for a service contract, and an API test cannot tell you whether a button is confusing to use. ## Problems that make functional tests unreliable Complex applications create many possible paths. You cannot test every combination, so use the requirements and failure history to choose cases that protect important decisions. Frequent product changes create another problem. If the expected behavior changes, update the test with the requirement. If only a CSS class changes, avoid tying a browser test to that class when a stable label or role is available. Data is a common source of false failures. Missing records, shared accounts, expired tokens, and leftover state can make the same test pass in one run and fail in another. Give each test the data it needs, and clean up state when the test finishes. ## How to know the suite is useful Read a test name and ask whether you can predict the behavior it protects. Read the assertions and ask what bug would make them fail. If the answer is "almost none," the test is noise even if it runs quickly. Also look at the failures that reach users. A suite should grow around real defects, unclear requirements, and high-risk workflows. More cases are not always better; better cases are better. ## Keep the next check close Functional testing is the practice of turning expected behavior into repeatable checks. Start with the requirement, include the unhappy paths, isolate the test data, compare results and side effects, then retain the valuable cases in regression runs. When you automate a check, keep one human question in mind: what would a user notice if this stopped working? That question usually leads to a clearer test than copying the implementation line by line. ## Functional Testing FAQ ### How does functional testing differ from non-functional testing? Functional testing checks if the features work as expected, focusing on _what_ the system does. Non-functional testing, on the other hand, evaluates aspects like performance, usability, and reliability, focusing on _how well_ the system performs. Functional testing would ensure a login works, while non-functional testing might measure how fast the login loads or if it maintains security standards. ### What is black-box testing, and how does it relate to functional testing? Black-box testing is a technique where the tester examines the functionality of the software without needing to understand the internal code or architecture. Functional testing is often conducted as black-box testing since it focuses on inputs and expected outputs rather than the underlying code. ### Why do we need both manual and automated functional testing? Manual testing is essential for scenarios where human judgment is necessary, like assessing the usability of a user interface. Automated testing, however, is faster and ideal for repetitive tasks or larger applications. Together, they ensure thorough and efficient testing coverage. ### How does Keploy enhance functional testing? Keploy is an open-source platform that simplifies automated testing by enabling teams to create test cases from real application interactions and logs. This approach allows developers to generate meaningful tests based on actual user behavior, making it easier to catch edge cases and validate core functionalities. Keploy also supports automated test case generation, reducing the manual work involved in traditional functional testing while ensuring tests remain relevant and effective over time. ## Find Elements in a Python List: 7 Methods with Code Examples URL: https://www.swapnoneel.site/blog/guide-finding-elements-in-a-list-using-python Date: 2024-11-18T00:46:33.000Z Summary: Python offers several ways to find values in a list, from in and index() to comprehensions, filter(), any(), and all(). Choose by the question you need to answer. Python lists keep items in order, and each item has a position called an index. You will often need to check whether a value is present, find its position, or collect every value that matches a rule. The right method depends on the question. Use `in` for a yes-or-no membership check. Use `index()` for the first position. Use a comprehension or `filter()` when you want a new collection. If you need repeated membership checks, a `set` may be a better data structure. You can also read more about related Python control flow in [https://keploy.io/blog/community/python-switch-case-how-to-implement](https://keploy.io/blog/community/python-switch-case-how-to-implement). ## Check membership with in The `in` operator returns `True` when Python finds the value in the list and `False` when it does not. It is the clearest option when you do not need the position. ```python my_list = [10, 20, 30, 40, 50] print(20 in my_list) # True print(100 in my_list) # False ``` For a list, Python checks items from left to right until it finds a match. That means the worst-case time is O(n), where n is the number of items. It is usually fine for a small list or a one-off check. ## Find the first position with index() `list.index(value)` returns the index of the first matching item. Python uses zero-based indexing, so the first item is at position `0`. ```python my_list = [1, 2, 3, 4, 2, 5] print(my_list.index(2)) # 1 ``` If the value is missing, `index()` raises `ValueError`. Check membership first when a missing value is expected to be normal, or handle the exception when you want to keep the lookup in one place. ```python value = 6 if value in my_list: print(my_list.index(value)) else: print("Element not found.") ``` This performs two searches when the value is present. For a small list that does not matter. If you are doing this repeatedly, use a loop or another data structure instead. ## Find every matching index `index()` stops at the first match. Use `enumerate()` inside a list comprehension when you need every position where the value appears. ```python my_list = [1, 2, 3, 4, 2, 5, 2] indices = [index for index, value in enumerate(my_list) if value == 2] print(indices) # [1, 4, 6] ``` `enumerate()` gives you the current index and value together. This is useful when the position matters, such as when you need to update, report, or remove matching entries. See [what `enumerate()` means in Python](https://keploy.io/blog/community/what-does-enumerate-mean-in-python) for another example. ## Filter by a condition Sometimes you are not looking for one exact value. You may want every number above a limit, every filename with a suffix, or every record that meets a business rule. `filter()` accepts a function and an iterable. It returns an iterator, so wrap it in `list()` when you need to print or reuse all of the results immediately. ```python my_list = [5, 10, 15, 20, 25] result = list(filter(lambda number: number > 15, my_list)) print(result) # [20, 25] ``` The lambda works here, but a named function is easier to read when the condition grows. ```python def is_large(number): return number > 15 result = list(filter(is_large, my_list)) print(result) # [20, 25] ``` ## Use a list comprehension A list comprehension is often the most readable choice for a new list based on a condition. It keeps the loop and the filter in one expression without hiding the result behind an iterator. ```python my_list = [1, 2, 3, 4, 5, 6, 7] even_numbers = [number for number in my_list if number % 2 == 0] print(even_numbers) # [2, 4, 6] ``` Use a normal `for` loop instead when the body needs several steps or side effects. A compact expression is not automatically clearer. ## Find the smallest and largest value The built-in `min()` and `max()` functions scan an iterable and return its smallest and largest item. ```python my_list = [100, 45, 78, 23, 56] print(min(my_list)) # 23 print(max(my_list)) # 100 ``` Both functions raise `ValueError` for an empty list. If an empty list is possible, check it first or provide a value that makes sense for your application. ```python my_list = [] if my_list: print(min(my_list)) else: print("The list is empty.") ``` ## Check whether values are truthy with any() and all() `any()` returns `True` when at least one item in the iterable is truthy. `all()` returns `True` only when every item is truthy. ```python my_list = [0, 1, 2, 3] print(any(my_list)) # True: 1, 2, and 3 are truthy print(all(my_list)) # False: 0 is falsy ``` These functions are most useful with a condition rather than raw numbers. ```python scores = [72, 81, 94] print(any(score < 50 for score in scores)) # False print(all(score >= 50 for score in scores)) # True ``` Python stops as soon as the answer is known. `any()` stops at the first truthy value, and `all()` stops at the first falsy value. ## Choose a set for repeated lookups Lists preserve order and allow duplicates. A set removes duplicates and is designed for membership checks. Building the set costs time and memory, but later lookups are average O(1) instead of scanning the list each time. ```python names = ["Mira", "Dev", "Mira", "Sam"] name_set = set(names) print("Sam" in name_set) # True print("Alex" in name_set) # False ``` Use a list when order or duplicates matter. Use a set when your main question is whether a value exists. A set cannot contain unhashable values such as lists, so that choice also depends on the kind of data you have. ## A quick decision guide Ask yourself what the result should be: - Need `True` or `False`? Use `in`, `any()`, or `all()`. - Need the first position? Use `index()` and handle a missing value. - Need every position? Use `enumerate()`. - Need a new list from a rule? Use a comprehension or `filter()`. - Need the smallest or largest item? Use `min()` or `max()`. - Need many membership checks? Consider a `set`. You do not need to memorize every method. Start by naming the result you want, then choose the expression that says it plainly. ## Further readings [https://keploy.io/blog/community/pull-api-data-python](https://keploy.io/blog/community/pull-api-data-python) [https://keploy.io/blog/community/when-to-use-a-list-comprehension-in-python](https://keploy.io/blog/community/when-to-use-a-list-comprehension-in-python) [https://keploy.io/blog/community/introduction-to-gitlab-python-api](https://keploy.io/blog/community/introduction-to-gitlab-python-api) ## FAQs ### **How can I find the last occurrence of an element in a list?** You can use `list.reverse()` to temporarily reverse the list, then use `index()`. Alternatively, use list slicing with `len(my_list) - 1 - my_list[::-1].index(value)` for an efficient approach. ### **Can I find elements using regular expressions in lists of strings?** Yes, you can use the `re` module for this. Iterate through the list and apply the regex search condition to filter matches. ### **Can I find elements based on complex conditions?** Yes, list comprehensions with multiple conditions or functions like `filter()` can be used to create flexible searches, even with custom logic. ### **How do I find elements in nested lists?** Use recursion or flatten the list with itertools’ `chain()` or custom functions. It involves traversing each level of nested lists to search for elements. ### **What is the performance of finding elements in a list?** Searching with `in` or `list.index()` has a time complexity of O(n) in the worst case. For faster searches, consider using sets or dictionaries, which have average O(1) lookup time. ![Thank you graphic for Python list blog](https://wp.keploy.io/wp-content/uploads/2024/11/Thank-you.webp) ## JavaScript Objects vs JSON: Are they same? URL: https://www.swapnoneel.site/blog/javascript-objects-vs-json Date: 2024-03-02T11:23:51.872Z Summary: JavaScript objects are live values your program can use; JSON is text for moving or storing data. Learn the syntax, type, behavior, and purpose differences. JavaScript objects and JSON look similar because JSON borrowed much of its shape from JavaScript object literals. They are still different things. An object is a value your JavaScript program can work with. JSON is text that follows a data format. You use objects while the program runs, and you often use JSON when data crosses a boundary, such as between a browser and a server. ## What a JavaScript object is An object groups related values under property names. Those values can be strings, numbers, booleans, arrays, other objects, or functions. A function stored on an object is usually called a method. ![JavaScript object concept illustration](https://cdn.hashnode.com/res/hashnode/image/upload/v1709377619396/b9dfbc39-cee3-4edc-9d95-ccb84b49a6fd.png) Think of a cup in a program. It can have a color, material, and weight, and it can have an action such as `wash()`. The object stores both the information and, when needed, the behavior associated with it. ```javascript const cup = { color: "black", design: "cylindrical", weight: 300, material: "glass", describe() { return `${this.color} ${this.material} cup`; }, }; console.log(cup.material); // glass console.log(cup.describe()); // black glass cup ``` The object exists inside the JavaScript runtime. You can read a property, call a method, add a property, or remove one. Its syntax is part of the JavaScript language. ## What JSON is JSON stands for JavaScript Object Notation, but you do not need JavaScript to read or write it. It is a text format used to represent data in a way that different languages can exchange. ![JSON data format illustration](https://cdn.hashnode.com/res/hashnode/image/upload/v1709377497454/4a07db50-35ce-476a-a183-5c8d82bdb8f1.png) JSON supports strings, numbers, booleans, `null`, arrays, and objects made from those values. It does not support functions, comments, `undefined`, or JavaScript-specific objects such as `Date` directly. ```json { "firstName": "John", "lastName": "Doe", "age": 30, "isEmployed": true, "address": { "street": "123 Main St", "city": "Anytown", "zipCode": "12345" }, "phoneNumbers": [ { "type": "home", "number": "212 555-1234" }, { "type": "office", "number": "646 555-5678" } ] } ``` Notice the quotation marks around every property name. JSON also forbids trailing commas. Those stricter rules make the text easier for another program to parse consistently. ## How objects and JSON meet When a JavaScript program needs to send an object through an HTTP request, it usually serializes the object into a JSON string. ```javascript const user = { name: "Asha", active: true, }; const body = JSON.stringify(user); console.log(body); // {"name":"Asha","active":true} ``` The result of `JSON.stringify()` is a string, not an object. You can send that string as a request body or save it in a file. When JSON comes back from a server, parse it before using it as an object. ```javascript const responseText = '{"name":"Asha","active":true}'; const parsedUser = JSON.parse(responseText); console.log(parsedUser.name); // Asha ``` `JSON.parse()` can throw a `SyntaxError` if the text is not valid JSON. If the text comes from an unreliable source, handle that failure instead of assuming the response is well formed. ## The differences that matter ### Behavior JavaScript objects can contain functions and can participate in the language's runtime behavior. JSON stores data only. If you stringify an object with a method, the function is left out. ```javascript const account = { name: "Asha", greet() { return `Hello, ${this.name}`; }, }; console.log(JSON.stringify(account)); // {"name":"Asha"} ``` ### Syntax JavaScript object literals can use unquoted property names when they are valid identifiers, single or double quotes for strings, trailing commas, comments around the code, and computed properties. JSON requires double-quoted property names and string values, and it does not allow comments or trailing commas. ### Types JavaScript has values such as `undefined`, `Date`, `Map`, `Set`, `BigInt`, and functions. JSON has a smaller set of data types. During serialization, `undefined` and functions may disappear from an object, while a `Date` is converted to a string through its serialization behavior. ### Purpose Objects help your JavaScript code organize and manipulate live data. JSON helps systems exchange a snapshot of that data. A JSON response becomes an object only after your code parses it. ## A small comparison | JavaScript object | JSON | | --------------------------------------------------- | ------------------------------------------------------ | | A runtime value in JavaScript | Text that follows a data format | | Can contain methods | Cannot contain functions | | Property names may be unquoted in an object literal | Property names must use double quotes | | Can use JavaScript-only values | Supports a smaller set of data types | | Read directly by JavaScript code | Parsed with `JSON.parse()` before normal object access | ## Which one should you use Use a JavaScript object while your application is reading, changing, or passing data around in memory. Use JSON when you need a portable text representation for an API response, request body, configuration file, or saved state. Do not call `JSON.stringify()` just because a value looks like JSON. First check the boundary. If another function in the same program expects an object, converting it to text only creates extra work and can remove values that JSON cannot represent. The sentence I keep coming back to is simple: an object is data your program can use, while JSON is text your program can exchange. Once you separate those two jobs, the similar-looking braces stop being confusing. ![Thank you graphic for JavaScript Objects vs JSON blog](https://cdn.hashnode.com/res/hashnode/image/upload/v1709377456629/8e775196-cf5e-4e28-bdc1-8bf7d419813f.png) ## How to Generate Random Numbers in JavaScript URL: https://www.swapnoneel.site/blog/javascript-random-number Date: 2024-11-01T01:20:01.000Z Summary: Use Math.random() for everyday variation, then build ranges, integers, booleans, choices, and shuffles around it. Use the Web Crypto API when randomness protects an identifier or secret. When you need a surprise in a game, a quiz, or a sample from a list, JavaScript gives you a starting point: `Math.random()`. It returns a decimal from 0 (included) up to 1 (not included). The number is pseudo-random, so it works for everyday variation but is not a security feature. The useful part is the small bit of math you put around that decimal. Once you understand that, random integers, choices, booleans, and shuffles all follow the same pattern. A related [JavaScript array filter guide on Keploy](https://keploy.io/blog/community/javascript-array-filter-method-guide) is available if you want to keep working with collections afterward. ## Start with Math.random() Call the function and store its result when you need to reuse it. The half-open range, written as `[0, 1)`, means 0 is possible while 1 is not. ```javascript const randomNum = Math.random(); console.log(randomNum); // A decimal greater than or equal to 0 and less than 1 ``` You should see a different-looking decimal on most runs: ![Console output of Math.random() generating floating-point number](https://wp.keploy.io/wp-content/uploads/2024/11/a8b9e0ce-0d09-4adb-ad38-f0e667c9-1.webp) That decimal is rarely the final shape you want. To create a value in another range, multiply it by the size of the range and then shift it by the minimum. ## Generate a number in a range This function returns a decimal greater than or equal to `min` and less than `max`: ```javascript function getRandomInRange(min, max) { if (min >= max) { throw new Error("min must be less than max"); } return Math.random() * (max - min) + min; } console.log(getRandomInRange(10, 20)); // A decimal from 10 up to, but not including, 20 ``` The expression `(max - min)` gives you the range width. Multiplying by `Math.random()` scales the result, and adding `min` moves it to the correct starting point. Here is the kind of output you might see: ![Console output of getRandomInRange function](https://wp.keploy.io/wp-content/uploads/2024/11/31462bb9-e5ae-48b7-bb57-3eb5495e-1.webp) ## Generate an integer in an inclusive range For a whole number, place `Math.floor()` around the scaled value. The `+ 1` matters when you want both endpoints included. Without it, the maximum value can never appear. ```javascript function getRandomIntInRange(min, max) { if (!Number.isInteger(min) || !Number.isInteger(max) || min > max) { throw new Error( "min and max must be integers, with min less than or equal to max" ); } return Math.floor(Math.random() * (max - min + 1)) + min; } console.log(getRandomIntInRange(1, 100)); // An integer from 1 through 100 ``` `Math.floor()` always rounds down. Since the scaled value is less than `max - min + 1`, the largest possible result after adding `min` is still `max`. The output will look similar to this: ![Console output of getRandomIntInRange generating random integer](https://wp.keploy.io/wp-content/uploads/2024/11/run-code-output.webp) ## Generate a random boolean A boolean needs only two outcomes. Comparing the random decimal with `0.5` gives you `true` about half the time and `false` the rest of the time. ```javascript function getRandomBoolean() { return Math.random() >= 0.5; } console.log(getRandomBoolean()); // true or false ``` You might see an output like this: ![Console output of getRandomBoolean function](https://wp.keploy.io/wp-content/uploads/2024/11/ed23a257-5fc0-4ed9-b5dd-a80ce6cd-1.webp) ## Pick a random array element Array indexes start at 0, so the largest valid index is `array.length - 1`. Multiplying by the length and flooring the result gives you an index that stays inside the array. ```javascript const colors = ["red", "green", "blue", "yellow"]; const randomColor = colors[Math.floor(Math.random() * colors.length)]; console.log(randomColor); // A color from the array ``` Here is an example of the resulting output: ![Console output selecting random element from array](https://wp.keploy.io/wp-content/uploads/2024/11/random-element.webp) ## Shuffle an array Sorting with a random comparator looks tempting, but it does not give every arrangement a fair chance. The Fisher-Yates algorithm is easier to reason about: start at the end, choose a random position from the part you have not shuffled, and swap the two values. ```javascript function shuffleArray(array) { for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } return array; } const numbers = [1, 2, 3, 4, 5]; console.log(shuffleArray(numbers)); // Outputs a shuffled array ``` This function changes the original array and returns it. If you need to keep the original order, pass a copy with `shuffleArray([...numbers])`. The console output could look like this: ![Console output of Fisher-Yates array shuffle algorithm](https://wp.keploy.io/wp-content/uploads/2024/11/shuffled-array-output.webp) ## Do not build secure identifiers with Math.random() The old hand-written UUID example is worth treating as a warning. It uses `Math.random()`, which is not designed for secrets and does not provide the guarantees you want from a UUID. In modern browsers, use the built-in UUID method instead: ```javascript const id = crypto.randomUUID(); console.log(id); ``` The existing screenshot shows the output from the older generator: ```javascript function generateUUID() { return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => { const r = (Math.random() * 16) | 0; const v = c === "x" ? r : (r & 0x3) | 0x8; return v.toString(16); }); } console.log(generateUUID()); // Outputs a random UUID ``` It can print a UUID-shaped string, but the shape alone does not make it safe for authentication tokens, password reset links, or other sensitive values. ![Console output of generateUUID function](https://wp.keploy.io/wp-content/uploads/2024/11/run-code-output2.webp) ## Use the Web Crypto API for security For passwords, tokens, keys, and other security-sensitive values, use the Web Cryptography API. `crypto.getRandomValues()` fills a typed array with secure random values: ```javascript function getSecureRandom() { const array = new Uint32Array(1); crypto.getRandomValues(array); return array[0]; } console.log(getSecureRandom()); ``` In browser code, `crypto` is available as the global Web Crypto object. In a Node.js project, use the crypto API provided by your Node version instead of assuming that `window` exists. ## The rule to remember Use `Math.random()` for ordinary variation, such as choosing a color or deciding which quiz question appears next. Use the Web Crypto API when someone could gain access, guess a value, or reset an account if the number is predictable. My caveat: randomness is easy to add and easy to misuse. Before reaching for a generator, decide whether you need a playful variation or a value that must resist guessing. For more JavaScript practice, see these related posts: [https://keploy.io/blog/technology/mastering-nyc-enhance-javascript-typescript-test-coverage](https://keploy.io/blog/technology/mastering-nyc-enhance-javascript-typescript-test-coverage) [https://keploy.io/blog/community/javascript-var-vs-let-vs-const](https://keploy.io/blog/community/javascript-var-vs-let-vs-const) ## My Second year in Tech URL: https://www.swapnoneel.site/blog/my-second-year-in-tech Date: 2024-11-08T21:33:04.263Z Summary: Exactly one year back from now, I published a blog where I shared my First year of Tech journey. In that blog, I’ve mentioned about how I got started, and balanced both development and DSA, while maintaining a consistent 9+ CGPA throughout the semest... Exactly one year back from now, I published a blog where I shared my First year of Tech journey. In that blog, I’ve mentioned about how I got started, and balanced both development and DSA, while maintaining a consistent 9+ CGPA throughout the semesters. Also, I shared how I got my breakthrough in freelancing and how I attended multiple hackathons and won all of them! If you haven’t read that blog, I would request you to [read it from here](https://swapnoneel.hashnode.dev/my-first-year-in-tech). Now, let’s talk about how much I progressed in the past one year, and see if I was able to meet my own expectations or plans that I had made for this year 👀, previously mentioned at the end of my last tech journey blog! ## The Journey ### November, 2023 As you have seen in my last tech-journey blog, the month of October was super productive for me, and that streak continued in November too. I managed to cross the 200+ problems mark on Leetcode and started doing Leetcode consistently. My goal was to solve at least one problem a day, and I made a routine for that,- whenever I’m opening my PC for the first time in a day, my target was to solve a single Leetcode problem at first and then move onto some other tasks! By the end of this month, I started participating in the Weekly Leetcode contests. This was among one of my to-do things from the first year of my journey! Alongside this, I was working on building a CRUD application called “Toile”, whose major purpose was to showcase my designing skill, alongside a functional backend. ![Toile CRUD web application design and dashboard](https://cdn.hashnode.com/res/hashnode/image/upload/v1731092158245/7fe5e731-cb6b-460d-ba3c-02720382b475.png) ### December, 2023 Completed the work on **Toile**! It took around a month to make it fully ready. But it turned out to be pretty good. You can check it out from here: [https://toile.vercel.app](https://toile.vercel.app) I started learning Dynamic Programming, because I noticed that there were a lot of questions coming from that during the contests. Also, I continued that habit of solving DSA problems regularly and managed to cross the 300+ mark of solving Leetcode problems. And, as I’ve solved all the Daily Problems of this month, I earned a badge for that. And most importantly, I got the opportunity at [Keploy](https://keploy.io) to work as a Community Evangelist. As a part of that role, I took an online session on “API Testing”, which went really really well, and also this was one of first talks to such a large audience! Additionally, I contributed to Keploy’s website and docs and helped them squash some bugs! Also, I ended this month with an amazing event DevFest Kolkata ‘24, where I learned about some new topics like Kotlin Multi-Platform, and was able to have a chat with multiple startup founders like Sakeet Thadad, Aloke Majumder from Hoichoi, Lakshya Mittal and got to experience an QnA session from Striver himself! Additionally, I designed a GitHub stats tracker, and as a part of that, I created a GitHub Wrap for myself! ![GitHub Wrap 2023 stats tracker interface](https://cdn.hashnode.com/res/hashnode/image/upload/v1731092247608/b63799de-6846-4b8b-8d91-3017ec5b280d.png) ### January, 2024 The year started with a bang! In the past few months, my freelancing was running a bit dry. But with the start of this year, I got two big projects from two different clients. One of them was a research-based technical writing and the other one was making the Career, Showcase and some other web pages for a small start-up! And, I maintained my consistency of solving DSA problems regularly and I was able to get the DPP Badge for this month too! ### February, 2024 The month started with a small contractual work for re-designing the UI of a start-up named Wizdom. So, there were a lot of revisions and talking extensively with the teams. As I was alone in the project, it was pretty exhilarating but I was able to deliver the task within two weeks. Although I’ve done UI/UX designing gigs before, but this was a bit different because it was a fully-working start-up rather than stand-alone clients! Also, me and my friend [Sam Maji](https://github.com/sammaji) decided to created a small SaaS project called, **InPoster**([https://inposter.vercel.app](https://inposter.vercel.app)) which will generate you viral LinkedIn posts with hashtags and other decorations just by taking some keywords from you about the topic. We challenged ourselves to build the project within one night, and we successfully did that, but it took some more days to improve some of the features and fix the bugs! ![InPoster viral LinkedIn post generator SaaS app](https://cdn.hashnode.com/res/hashnode/image/upload/v1731092318237/24a382a5-96bb-4d11-b7aa-108afad301e2.png) ### March, 2024 I updated my old portfolio site and revamped it completely. I focused a lot on the design this time and added a lot of animations using GSAP. You can check it out from here: [https://swapnoneel.vercel.app](https://swapnoneel.vercel.app) Also, this month, I completed solving 500 problems on Leetcode and got really consistent in participating in the contests, and achieved my highest rating of 1662 points! And one notable thing, I got an offer as a Front-End Engineer from a renowned start-up, but had to reject it because they wanted me as a full-time on-site employee, which was not possible for me because of my college and current scenario! ### April, 2024 This was the month of my end-semester exams, so definitely I was invested in that. But, I didn’t stopped practicing and solving DSA problems, and completed learning Graphs during this period of time. Also, I contributed a lot in [Keploy](https://github.com/keploy)’s Open-Source repositories this month, and created a sample app for them using Flask and MongoDB. Not only that, due to my excessive contribution to their projects, I was able to become one of the Top Contributors for the month! ![Top Keploy contributors of April 2024 leaderboard](https://pbs.twimg.com/media/GMZozT9b0AE6tAh?format=jpg&name=4096x4096) ### May, 2024 Maybe this was my most favorite month of the year! Started the month with a large freelancing work. It was related to image classification based on the locations from Google Maps (It may sound gibberish, but sadly I can’t elaborate more than this because of strict MoU). I had to get my hands dirty with image optimization, had to find innovative ways to store the images and make it available for my ML model at a steady stream. As I was on post-exam vacation, I was able to invest a lot of time in this and was able to deliver this project way before the deadline! Also, I worked on improving a old project of mine called [MAKAUT Buddy](https://makaut-buddy.vercel.app) which I had mentioned in my last blog. I revamped the Dashboard section completely, and made it more functional and feature-rich. Additionally, I wanted to create something unique and decided to create some `npm` packages. So, I created my first package called `swapnoneel`, which will give a short bio about me and all of my social handles when you run `npx swapnoneel` from your terminal. This was just the beginning, because I planned to create way more complex projects than this! And the best part about the month was that I got a remote opportunity to work as an **Intern with Keploy**, because of my extensive contributions to their projects in the recent times 🥳 ### June, 2024 Started this month while making a `npm` package called [`get-response`](https://www.npmjs.com/package/get-response). It’s a terminal based application that interacts with the Google's Gemini API to generate content based on the user input. It allows you to ask questions directly or provide context from files, images or directories, and get the response in a simple and easy to understand interface. Also, you can automate some terminal commands by prompting for the task. And additionally, we also have support for responses from Stack Exchange sites like Stack Overflow. It got over thousands of downloads and got extremely positive response. I incrementally updated it throughout the month and kept on adding more and more features to it. Delivered a python-based ML project for a research scholar, and as a part of it I had to explore Streamlit a bit, and while doing so, I made this app using Gemini’s API, that can convert your provided design into working HTML and CSS code. You can try it for yourself from here: [https://ui-to-code.streamlit.app](https://ui-to-code.streamlit.app) ![UI to Code Streamlit app preview](https://pbs.twimg.com/media/GQEH-MfaIAAJRVG?format=jpg&name=large) ### July, 2023 In my previous blog, I mentioned that I attended Eastern India’s biggest hackathon “Hack 4 Bengal 2.0”, but sadly we had to return mid-way because of an unavoidable circumstance. So, it was the time to make it a success in their next iteration, “[Hack 4 Bengal 3.0](https://www.hack4bengal.tech)”. So at very end of last month, me along with my other two teammates [Tuhin Poddar](https://github.com/Tuhin114) and [Sam Maji](https://github.com/sammaji) attended the hackathon. We created a cloud-based AI powered terminal called “[Term AI](https://term-ai.vercel.app)”, that let’s you code from anywhere at anytime from whichever device you want, which frees you from the hassle of carrying a laptop or relying on a desktop while travelling. It was really fun to make this project, because at this point we are making something unique that can be used by folks in their day-to-day life! ![Term AI cloud-based AI terminal interface](https://cdn.hashnode.com/res/hashnode/image/upload/v1731093334429/8cc5cee5-a976-4c33-a5dc-563e0ae4d62e.png) And, we ended up **winning the prize of second runner-up** among all of the 300+ participants. It was a really enjoyable moment for us, because from failing to stay throughout the hackathon in last year to winning it in this year, felt like a redemption arc for me! And it genuinely felt extremely good! ![Hack 4 Bengal 3.0 second runner-up award achievement](https://cdn.hashnode.com/res/hashnode/image/upload/v1731167476958/0c89adf8-a6cf-4c66-844a-e23159b3efa2.png) Also, I organized an open-source bootcamp under the hood of Keploy, called the “**Keploy Rain of Code**”, which led to an massive upsurge of open-source contributors for Keploy, and it was an unique experience for me to guide and manage so many folks all at once, and giving each one of them equal attention! The program was a great success and we were able to get a lot of new quality contributors. ### August, 2024 This was again the month of our end-semester exam, and I had to invest the majority amount of time on my college academics. So, it was kind of stale from my tech side. I just delivered a small technical writing project during this month. But, I kept in touch with DSA and continued solving more and more problems just like I was doing consistently throughout this period. ### September, 2024 This month was a bit hectic for me, as I was dealing with some personal problems, and also got two large development projects at the same time. So, I had to work for long hours straight. There were some days in these month where I didn’t even got the time to sleep. But the good thing is, I was able to deliver those projects successfully (and although, I can’t talk about the nature of the project or its tech-stack, but I can say it was unique and was an extremely enriching experience for me). ### October, 2024 This month, I created a RAG-based AI chatbot for Keploy, which is being trained on the Keploy docs and gives response for each and every Keploy-related question that you ask it! Other than this, I worked on delivering a couple of articles for a client and worked on creating the code documentation for an early-stage startup. Also, I worked on making a website design as a part of another gig. ## Bonus I have tried to mention most of the important stuff here. But I want to mention/clarify some things that weren't mentioned in the timeline or haven’t got reflected enough: - Solving DSA problems was a integral part of my journey, and almost everyday I practiced problems on Leetcode, starting from November, last year. And I’ve been highly consistent in doing that! ![LeetCode problem solving streak and statistics chart](https://cdn.hashnode.com/res/hashnode/image/upload/v1731097936631/9892cf9a-b21c-4b9d-9787-749937c80916.png) - I’ve mentioned about only the important or substantial freelancing works that have influenced my journey and have taken quite a few days or weeks to finish. Other than the mentioned ones, I’ve done a lot of other works in the freelancing space, which aren’t that worthwhile to mention. - In few places, I’ve mentioned about what I’ve done while working within Keploy. But please take note that these were just a few things that I really loved working on, or was worthy of mentioning here. Being a DevRel is a lot more than this! - I invested majority of my freelancing money in stocks or spent them in buying assets. And also, I gifted myself a Nothing Phone (2a) this year, and also built a completely new work-setup for me in our house! Other than these, I paid the semester fees this year, all by myself; and also have taken care of my closed ones. - In my last year’s blog, I had mentioned that I was mentoring my juniors and guiding them, which I did consistently throughout this year too, but I didn’t felt the need to mention it here and elaborating on that front. If you are eager to learn more about it, you can go through our [LinkedIn page](https://www.linkedin.com/company/the-async-devs/). - I was active on Twitter(X) throughout this time, and was able to grow my follower count up to 1800 people. You can check out [my X / Twitter profile](https://x.com/swapnoneel123)! And tried to be as active on LinkedIn as possible, you can check out [my LinkedIn profile](https://www.linkedin.com/in/swapnoneel-saha-14a3161b6/). ## Was I able to meet my expectations? Previously, in my first year of tech journey, I mentioned about some of the plans or goals that I want to achieve in my second year. Among them, I targeted to solve over 600+ Leetcode problems by the end of second year, and I was able to go way beyond that, and have solved just about 750 problems! I wanted to start delving deep into the domain of AI/ML, but sadly enough I was able to scratch only the surface. But I achieved my plan of making some SaaS products with my knowledge of AI/ML and Web Development, through InPoster, TermAI and Get Response. I planned to attend more hackathons, but I attended only one of them. This was an well thought out decision because rather than focusing on quantity, I had decided to focus on the quality and attended the hackathons that felt worthy of my time, efforts and skills. Although, I was prepared to attend two more hackathons, but due to exams and other schedule issues, I wasn’t able to participate in them. Also, I planned to delve into the topics of System Design and Operating System, which I did in this year. And, my plan of grabbing an internship and contributing to large-scale open-source projects also got fulfilled through Keploy. And finally, my plan of guiding my juniors was fruitful and I have been guiding them throughout this whole time, and have expanded our scope in doing so! ## My Plans for the Third Year I want to focus on learning AI and ML in-depth this year, which I wasn’t able to fulfill completely in my last year. And this is my highest priority right now. Also, I want to create some innovative projects on the way while learning it. I want to focus on my college academics a bit more, which I’ve completely neglected throughout my second year. And focus on the core subjects which will come really handy in future. I’m also looking forward for a full-time developer role or to get an SDE/SWE internship from a renowned organization. Also, I would like to participate in more and more coding contests, because although I had consistently started attending them this year but failed to do so because of timing issues. So, I would consider switching the platform and attend more and more contests as possible. Other than these, I want to face more challenges and learn new stuffs that will come across my way. And I plan to continue doing what I’ve been doing throughout these months, and maintain the same level of consistency! ## Conclusion And that's a wrap!! But if you have any queries regarding my journey or any suggestions for my future, I would love to hear that from you in the comments. And if you want to stay connected with me, you can follow me here or on my other social handles. Thank You for reading about my journey, have a nice day ahead!! ![Thank you graphic for second year in tech blog](https://cdn.hashnode.com/res/hashnode/image/upload/v1716652301849/8327a90c-373e-4837-9102-e67bb38def0c.png?auto=compress,format&format=webp) ## Installing Node.js and managing it's versions were this easy? URL: https://www.swapnoneel.site/blog/nodejs-npm-nvm Date: 2024-05-24T14:42:50.142Z Summary: Introduction We all know that, Node.js is the runtime environment for JavaScript that executes JavaScript code outside the web browser. And, npm stands for Node Package Manager and is the default package manager for Node.js and is also a platform for... ## Introduction We all know that, **Node.js** is the runtime environment for JavaScript that executes JavaScript code outside the web browser. And, **npm** stands for Node Package Manager and is the default package manager for Node.js and is also a platform for managing JavaScript packages. It provides a command-line interface (CLI) for interacting with the npm registry, which hosts thousands of open-source libraries and modules. ## How to install node.js and npm locally? Based on the operating systems, the installation process is different and varied. That's why, I'm classifying them here so that you folks don't get confused between them. ![Node.js architecture and runtime overview](https://cdn.hashnode.com/res/hashnode/image/upload/v1716554172337/62142e92-545a-4b6b-9b5e-b018bcf9d0e8.png) ### Windows 1. **Download the Installer**: - Go to the official Node.js website: [nodejs.org](https://nodejs.org/). - Download the LTS (Long Term Support) version, because it happens to be the most stable version. 2. **Run the Installer**: - Open the downloaded `.msi` file and run the installer. - Follow the installation steps, and please **make sure to check the box that says "Install Node.js and npm."** ### _macOS_ 1. You have to install **Homebrew** if you haven't already. To do that, open the terminal and run: ```bash /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" ``` 2. Now, we can install `node.js` and `npm` with Homebrew: ```bash brew install node ``` ### _Linux_ It's extremely easy to install `node.js` and `npm` in Linux. Just run these two commands in the terminal: ```bash sudo apt update sudo apt install nodejs npm ``` ## Verify your installation You can verify the installation of `node.js` and `npm` by running the following two commands in your terminal: ```bash node -v npm -v ``` > _If you are on Windows, Powershell may cause some issues, so try to use Command Prompt in that case. It will work!!_ ![Terminal verification of node and npm versions](https://cdn.hashnode.com/res/hashnode/image/upload/v1716553688790/eaf89f25-9858-410e-9ebc-082022a6697e.png) ## Managing node versions using NVM NVM stands for Node Version Manager and it's a command-line tool that helps us to manage and switch different versions of Node.js with ease and convenience!! ![NVM Node Version Manager overview banner](https://cdn.hashnode.com/res/hashnode/image/upload/v1716554276508/db2ae459-9cd3-41fe-837f-29c2ea3d8671.png) 1. ### Check available node versions Before installing any node version, let's first check the available Node versions. To do that, we can simply run the following command in our terminal: ```bash nvm ls available ``` After running this, you get something like this: ![Terminal output of nvm ls available command](https://cdn.hashnode.com/res/hashnode/image/upload/v1716555882030/1d1e966c-c139-4c92-a68e-a64cbde6b661.png) 2. ### Installing the latest node version To install the latest version of node.js, you can simply run this command, ```bash nvm install latest ``` But remember, it's always better to install the LTS (long-term support) version of node, because it's less buggy and is overall more stable!! To install the LTS version of Node, run the command,- ```bash nvm install lts ``` 3. ### Install multiple Node versions One of the most interesting part of NVM is you can install multiple versions of Node at the same time and use any of them based on your convenience!! For this, nvm has the `nvm install` command. You can install specific versions by running this command followed by the version you want. For example, ```bash nvm install 18.17.0 nvm install 20.11.1 nvm install 20.12 ``` Also, as NVM follows semantic versioning, you can install v18.17 and use any of the following version under 18.17, like 18.17.0, 18.17.1, etc. Here, 18 represents the major version, 17 represents the minor version, and 1 represents the patch version!! 4. ### Installing specific Node versions You can install any specific node version, by running this command,- ```bash nvm install ``` Replace the `` with your desired Node version. But, to ensure that your given version is valid, make sure to run `nvm ls available` and put a correct version from the list!! Also, once you install a version of Node, the corresponding version of NPM is also installed alongside with it. So you don’t need to install NPM separately!! 5. ### Check installed Node versions in your system To check the list of all node versions that you have installed on your system, you can simply run,- ```bash nvm list ``` And, you will see a response like this: ![Terminal output of nvm list showing installed Node versions](https://cdn.hashnode.com/res/hashnode/image/upload/v1716560504142/21e3f56e-e0d9-415f-96e9-816e2ccb40da.png) 6. ### Switching Node versions As you can see in the previous image that I'm currently using `20.13.1`. Now, if I want to switch my version to another one like `18.17.0`. I can simply use the following command: ```bash nvm use 18.17.0 ``` In your case, you can put your desired version in place of `18.17.0`, but first make sure it is a valid version number and it is installed on your system!! 7. ### Uninstall a Node version To uninstall an already installed Node version that you no longer think is useful, you can do that by running the command,- ```bash nvm uninstall ``` Replace the `` with your desired and installed Node version. ## Conclusion > TLDR: This article provides a comprehensive guide on installing Node.js and npm across different operating systems (Windows, macOS, and Linux), verifying installations, and managing Node.js versions using NVM (Node Version Manager). It covers steps to install specific Node.js versions, switch between versions, and uninstall versions when no longer needed. The guide emphasizes the importance of using the LTS (Long Term Support) version for stability and includes detailed commands for each process. Well, that's a wrap for now!! Hope you folks have enriched yourself today with lots of known or unknown concepts. I wish you a great day ahead and till then keep learning and keep exploring!! ![Thank you graphic for Node.js npm NVM guide](https://cdn.hashnode.com/res/hashnode/image/upload/v1716561409967/1916dd93-56c0-4df1-8be1-19b75342aad1.png) ## Publishing your own NPM Package is too simple!! URL: https://www.swapnoneel.site/blog/publish-your-own-npm-package Date: 2024-05-25T15:55:48.764Z Summary: Introduction Isn't it cool if you can run command like npx in someone's terminal, and they can read about you, your works, and find details like your social media handles, right at their terminal? Or maybe you have created some cool React ... ## Introduction Isn't it cool if you can run command like `npx ` in someone's terminal, and they can read about you, your works, and find details like your social media handles, right at their terminal? Or maybe you have created some cool React component, wouldn't that be even more cooler if other folks can use them in their projects just by installing an `npm` package? I think you have got the drill already... Publishing your own NPM package is really cool and can help a lot of folks globally!! So, before we start about how to publish your own NPM package, I think you should run these two commands first at your terminal `npx swapnoneel` and `npx get-response`, to properly understand what I mean by "cool" and "helpful"!! ## Create your NPM account This is the most obvious step, if you want to publish your own package!! Just head over to [https://www.npmjs.com](https://www.npmjs.com) and create your account. Also, remember to do two-factor authentication of your account. You can use an Authenticator app like [TOTP Authenticator](https://play.google.com/store/apps/details?id=com.authenticator.authservice2&hl=en&gl=US)!! We will talk later, about how and when to use our account. For now, let's continue to the next step!! ## Choose the name of your NPM package The very first thing you need to do before creating your package is to choose a name. This is important because your package needs to have a unique name and you cannot choose a name that has been used already. After you choose a name, you've to go to the [NPM registry](https://www.npmjs.com/) and run a search. Be sure there's no exact match to the name you chose or a match that is too similar. Otherwise, you have to try choosing a different name!! ## Let's start creating the NPM package As I've stated in the title itself, that creating your own NPM package is extremely simple; now you will understand, why! ### Install Node and NPM If you don't have Node installed already, you should go and refer to [this article](https://swapnoneel.hashnode.dev/nodejs-npm-nvm) and install it. In the article, you will find the step-by-step guide about how to install and maintain the different Node versions!! ### Initialize git repository Create a new project folder for your package and navigate into the folder. Then, run the following command in your terminal: ```bash git init ``` This will help us track the changes in our package. Also, make sure that it remains hosted on GitHub!! ### Initialize NPM in your project To do this, navigate to the root directory of your project and run the following command: ```bash npm init ``` Now, you will prompted to give some answers, I'm mentioning the utility of each one of them: - `package-name`: It should be the name of your package that you want to publish. Also, remember that it must be lowercase and may only include hyphens. - `version`: The initial value is `1.0.0`. You may change it to `0.1.0` and release the full version `1.0.0` later!! You will be updating the version number each time when you update your package using [semantic versioning](https://www.freecodecamp.org/news/semantic-versioning-1fd6f57749f7/). - `description`: You can provide a description of your package here, explaining about what your package does and how to use it. - `entry point`: The entry file for your code. The default value is `index.js`. - `test command`: Here, you can add the command you want to run when a user runs `npm run test`. To keep things simple, you may keep it blank for now!! - `git repository`: The link to your remote repository on GitHub. It's optional, but it's better if you can add the GitHub repository link here!! - `keywords`: Add relevant keywords that will help others find your package on the NPM registry. It's optional too, and you can leave it blank for now!! - `author`: Add your name. - `license`: You can add a license or use the default license, Internet Systems Consortium (ISC) License. Once you provide all these information, a file will be created called `package.json` that will kinda look like this: ![package.json file generated after running npm init](https://cdn.hashnode.com/res/hashnode/image/upload/v1716639984976/535670bf-65cd-4641-9b48-1be0daef046b.png)
💡
Additionally, you should modify the package.json file to add "type" : "module" which will enable you to write the JavaScript code in ECMAScript modules format. This allows for cleaner code organization, code reusability, and better encapsulation.
### Write your code Now, you can go ahead and write your code for the package. For this tutorial, I will be writing my code in the `index.js` file. **💡 Reminder!!** _Please add the following line at the start of the code because it tells the operating system to use the Node.js interpreter to execute the script. It allows us to run the script by simply typing its filename, without having to specify the full path to the Node.js interpreter._ ```javascript #!/usr/bin/env node ``` Now inside the `index.js` file, write the code for your package. Here, I will be creating a simple package called `test`. This package will print the string `"Reminder to follow Swapnoneel on Twitter at https://x.com/swapnoneel123"` in the terminal. ![index.js code implementation printing reminder text](https://cdn.hashnode.com/res/hashnode/image/upload/v1716648802856/1df1543f-5bb4-43e6-affa-3f4461378f83.png) ### Create an executable script Now, we have to make this `index.js` file executable and assign it to a command, so that whenever we run that command, we get the result output-ed by `index.js` . For doing that, we have to add this in out `package.json` file: ```json "bin": { "test": "./index.js" }, ``` So, after all the changes, our `package.json` file should look somewhat like this: ```json { "name": "test", "version": "1.0.0", "description": "just to test how to publish npm package", "main": "./index.js", "type": "module", "bin": { "test": "./index.js" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "git+https://github.com/Swpn0neel/test.git" }, "author": "Swapnoneel Saha", "license": "ISC", "bugs": { "url": "https://github.com/Swpn0neel/test/issues" }, "homepage": "https://github.com/Swpn0neel/test#readme" } ``` ### Make the script executable Now, to make this script executable, so that works perfectly, we have to run this command in our terminals: **Linux** ```bash chmod +x index.js ``` **Windows** _Note that, for this to work perfectly, you need to have_`git`_installed on your system!!_ ```bash git update-index --chmod=+x index.js ``` ## Test your NPM package Testing ensures that your NPM package works as expected. To do that, first navigate to the root of your project. Then, run the following command: ```bash npm link ``` This will make your package available globally. And you can require the package in a different project to test it out. For that, you go outside the root directory and try running the following command to add the package you have just created: ```bash npm link ``` And, you'll see something like this: ![Terminal output of npm link command testing package locally](https://cdn.hashnode.com/res/hashnode/image/upload/v1716650345968/b8038b49-3ee6-4ce2-8e78-f61b04b60a84.png) Now you can run your package, and it should work perfectly!! ![Terminal output of running local package command](https://cdn.hashnode.com/res/hashnode/image/upload/v1716650425160/b8d12272-5801-4c13-87fa-243a88244d8f.png) ## _Publish Your NPM Package_ To publish your package on the NPM registry, you need to have an account. And, I hope you have created that previously!! So, now it's time to open your terminal and run the following command in the root of your package: ```bash npm login ``` You will get a prompt to enter your `username` and `password`. If login is successful, you should see a message like this: `Logged in as on https://registry.npmjs.org/.` You can now run the following command to publish your package on the NPM registry: ```bash npm publish ``` Note that, during these steps you may need to use your authentication app to authenticate the procedure!! And so if you have been following along, then congratulations! You just published your first NPM package. And, you can visit the [NPM website](https://www.npmjs.com/) and run a search for your package. You should see your package show up in the search results. ## Conclusion And now, you can probably boast about your coolness, because yes, your tool can be accessed anywhere in the world in anybody's terminal!! If you want to know how vast tools you can make through this, head over to [Get Response](https://www.npmjs.com/package/get-response), which is a node.js based command-line interface (CLI) tool that uses the Google's Gemini to generate content based on the user input. This tool allows you to ask questions directly or provide context from files or directories, and get the response in a simple and easy to understand interface. And finally, thank you for reading the blog! I hope you found it informative and valuable. For more information, follow me on [**Twitter (swapnoneel123**](http://twitter.com/swapnoneel123)**)** where I share more such content through my tweets and threads. And, please consider sharing it with others on **Twitter** and tag me in your post so I can see it too. You can also check my [**GitHub (Swpn0neel)**](https://github.com/Swpn0neel) to see my projects. I wish you a great day ahead and till then keep learning and keep exploring!! ![Thank you graphic for npm publishing guide](https://cdn.hashnode.com/res/hashnode/image/upload/v1716652301849/8327a90c-373e-4837-9102-e67bb38def0c.png) ## The 3 Most Powerful Functions in JavaScript URL: https://www.swapnoneel.site/blog/the-3-most-powerful-functions-in-javascript Date: 2024-03-10T12:14:24.268Z Summary: map(), filter(), and reduce() cover three common array tasks: transforming values, keeping matches, and combining items into one result. An array is useful because it keeps related values together. The awkward part begins when you need to change every value, keep only some values, or combine all of them into one result. That is where `map()`, `filter()`, and `reduce()` fit. They are higher-order functions because they receive another function as an argument. You describe the operation once, and JavaScript calls it for the array values. You will see these methods in browser code, server code, and UI libraries such as React. The syntax is compact, but the idea underneath is simple: transform, select, or combine. ## The map() function Use `map()` when every item should produce a corresponding item in a new array. The original array stays unchanged, and the new array has the same number of positions unless your callback changes the value to something else. ### Syntax ```javascript array.map((element, index, array) => { // return the new value for this element }); ``` The callback receives the current `element`, its `index`, and the full `array`. Most of the time, the element is enough. ### Example Here, each number is multiplied by itself. The input remains `[1, 2, 3, 4, 5]`, while `squaredNums` receives the new values. ```javascript const nums = [1, 2, 3, 4, 5]; const squaredNums = nums.map((num) => num * num); console.log(squaredNums); // [1, 4, 9, 16, 25] ``` The browser console shows the transformed array: ![Browser console output of JavaScript map() function](https://cdn.hashnode.com/res/hashnode/image/upload/v1710071277175/f965ca2a-4cbb-4009-900e-22673b31f60f.png) If your callback does not return a value, the new array contains `undefined` for those positions. That small mistake is common when a block-bodied arrow function uses braces but forgets `return`. ## The filter() function Use `filter()` when you want a smaller array containing only the items that pass a test. The callback should return a truthy or falsy value. JavaScript keeps the item when the result is truthy and skips it when the result is falsy. ### Syntax ```javascript array.filter((element, index, array) => { // return true to keep the element }); ``` The callback receives the same three arguments as `map()`, but its job is different. You are answering "keep this item?" rather than creating a replacement value. ### Example This callback keeps positive numbers and removes the negative ones: ```javascript const nums = [1, -2, 3, 4, 5, -6, -7]; const positiveNums = nums.filter((num) => num > 0); console.log(positiveNums); // [1, 3, 4, 5] ``` Here is the corresponding browser output: ![Browser console output of JavaScript filter() function](https://cdn.hashnode.com/res/hashnode/image/upload/v1710071131323/f9ec94cf-bdde-4df3-a116-85267d8b26aa.png) An empty result is not an error. It simply means that no item passed the test. That makes `filter()` useful for searches, permission checks, and lists where the visible items depend on a condition. ## The reduce() function Use `reduce()` when an array should become one final value. That value can be a number, string, object, or even another array. Because `reduce()` can do many jobs, it is also the method most likely to become hard to read. ### Syntax ```javascript array.reduce((accumulator, currentValue, index, array) => { // return the accumulator for the next iteration }, initialValue); ``` The `accumulator` carries the result forward. `currentValue` is the item being processed now. The `initialValue` gives the accumulator a known starting point, which also keeps the behavior clear when the input array is empty. ### Example To multiply all the numbers, start the accumulator at `1`. The first pass multiplies `1` by `1`, the next pass multiplies that result by `2`, and so on until the final product is returned. ```javascript const nums = [1, 2, 3, 4, 5]; const product = nums.reduce( (accumulator, currentValue) => accumulator * currentValue, 1 ); console.log(product); // 120 ``` The browser console shows the final value: ![Browser console output of JavaScript reduce() function](https://cdn.hashnode.com/res/hashnode/image/upload/v1710072423789/a7c51e1d-1d4a-4fa9-b9ea-07f90564ecaf.png) When the accumulator is an object or an array, return that same accumulator after updating it. Also, do not use `reduce()` just because you can. A short `map()` or `filter()` chain often tells the reader more about your intent. ## Choosing the right method Ask what should happen to the array. If each input needs a corresponding output, use `map()`. If some inputs should disappear, use `filter()`. If everything must become one value, use `reduce()`. You can combine them when the steps are genuinely separate. For example, `orders.filter(...).map(...)` first removes orders you do not want and then formats the remaining ones. Give each callback a useful name if the expression stops being easy to read. My caveat is that `reduce()` is not automatically the most advanced choice. I would rather read two obvious passes than decode one clever accumulator, especially when another person has to debug it later. ![Thank you graphic for JavaScript higher order functions blog](https://cdn.hashnode.com/res/hashnode/image/upload/v1710072584207/88548bcf-0b8a-42e6-a3e1-b81c1042b7b6.png) ## The Impact of AI on Code Commenting and Software Documentation URL: https://www.swapnoneel.site/blog/the-impact-of-ai-on-code-commenting-and-software-documentation Date: 2024-11-15T00:21:42.000Z Summary: AI can draft comments and documentation, but it cannot decide whether they explain the right behavior or the reason behind it. Use a review loop before committing generated text. Comments and documentation answer questions that the code cannot answer by itself. A function can show how it calculates a value, but it may not show why the product needs that rule, which input is trusted, or what must stay true when the code changes. That information is easy to postpone. Then a few months pass, the original author is busy, and a harmless-looking change turns into archaeology. AI tools can help you write a first draft, but they cannot take responsibility for whether that draft describes the code honestly. ## What good documentation is supposed to do Start with the reader. Someone opening a file should be able to understand the purpose of the module, the assumptions around its inputs, and the unusual decisions that would otherwise look like mistakes. Comments are most useful when they explain a reason or a constraint. They should not narrate obvious syntax. This comment adds little information: ```python # Add one to the count. count += 1 ``` This one gives the next reader something they could not get by staring at the line: ```python # Keep the first event in the count because the reporting API uses one-based totals. count += 1 ``` Documentation outside the code has a different job. A README can explain how to run a project. An API document can describe a request and response. A comment belongs near the decision it explains, so it should stay short enough to update when that decision changes. ## Where AI helps AI assistants are useful when the work is repetitive and the boundaries are clear. They can summarize a file, suggest a docstring, or turn a function signature into a rough explanation. Tools such as GitHub Copilot and Amazon CodeWhisperer can also propose comments while you write code. For example, an assistant may draft this explanation for a recursive factorial function: ```python # This function calculates the factorial of a given integer. # It uses recursion to find the product of all positive integers up to n. def factorial(n): if n <= 1: return 1 return n * factorial(n - 1) ``` The draft is a starting point, not a completed review. It does not say what should happen for a negative value, and it assumes that recursion is the right detail for the reader. You still need to decide whether the comment belongs, what contract the function should have, and how that contract is enforced. AI can also summarize a long file before you read it closely. That can help you find the main entry points, but a summary is a map, not proof. Check it against the code before you copy it into a README or an issue. ## When a generated comment is dangerous The code tells the model what exists. It does not always tell the model why it exists. A generated sentence can sound confident while inventing an intention, missing an edge case, or describing an old version of the implementation. That matters most around permissions, money, security checks, retries, data retention, and compatibility rules. A wrong comment in one of those places can push the next person toward the wrong fix. There is another failure mode: stale truth. If a comment repeats what the next line does and that line changes, the comment can drift without anyone noticing. The reader then has two conflicting versions of the program. My caveat is simple: I would rather leave a small section undocumented for a moment than merge a polished explanation that nobody verified. A plain TODO that names the missing decision is more honest than an incorrect paragraph. ## A review loop that works Use the generated text in a short loop. First, ask the tool for a draft that focuses on behavior and assumptions. Then read the code yourself and delete anything that merely repeats syntax. Finally, run the tests and update the comment if the test exposes a different contract. Keep the prompt close to the question you need answered. "Explain this file" is broad. "Describe why this cache entry is rejected when the version changes" gives the tool a narrower task and gives you a clearer result to review. Do not send secrets, private data, or proprietary implementation details to a service unless your project allows it. Also check generated comments for sensitive names, internal URLs, and details that should not be public. Documentation can leak information even when the code path itself is protected. ## Can AI help with software testing too The same review rule applies to generated tests. A test is useful when it checks a behavior you care about, not when it only makes the coverage number larger. [Keploy](https://keploy.io) is mentioned here because it can generate test cases and stubs or mocks for unit and integration testing from API interactions. If you use a tool like that, inspect the captured inputs, remove sensitive values, and confirm that the expected response represents a real contract before keeping the test. ![Keploy documentation logo](https://keploy.io/docs/img/keploy-logo-dark.svg) The image above belongs in the testing section because the connection is practical: documentation tells you what a boundary should do, and a test can check that the boundary keeps doing it. The tool can save typing, but you still own the test's meaning. ## The part AI cannot sign off on AI is good at producing a plausible first pass. You are still responsible for the final sentence. Check every claim against the code, the tests, and the project rules, then leave a comment only when it will help the next reader make a better decision. That is the useful division of labor. Let the tool handle the blank page and repetitive wording. Keep the judgment, context, and security review with the person who understands what the software is allowed to do. For more writing about software, follow me on [Twitter (swapnoneel123)](http://twitter.com/swapnoneel123). You can also browse my [GitHub (Swpn0neel)](https://github.com/Swpn0neel) projects. ![Thank you graphic for AI code commenting blog](https://wp.keploy.io/wp-content/uploads/2024/11/Thank-you.webp) --- ## FAQ’s ### Can AI-generated documentation fully replace human effort? No, AI-generated documentation should complement, not replace, human effort. While AI tools provide a great starting point, developers are needed to review, refine, and ensure that comments accurately reflect the code’s purpose. Human oversight ensures clarity, avoids inaccuracies, and accounts for context and project-specific nuances that AI might miss. ### Does using AI for documentation affect project security? Potentially, yes. AI tools may inadvertently generate comments that expose sensitive logic or highlight potential weaknesses in the code. It’s essential to review all AI-generated comments for security implications and ensure they align with the project’s security protocols. ### Are AI documentation tools customizable for specific project needs? Yes, many AI-powered documentation tools offer some degree of customization. Developers can configure rules for comment styles, preferred templates, or even teach the model about specific code structures common to the project or organization. This ensures more accurate, tailored output. ### Can AI-generated documentation be used as a learning tool? Absolutely! For newer developers or team members, AI-generated documentation can serve as a useful starting point to understand unfamiliar codebases. It can provide immediate context and overviews, aiding faster comprehension. Paired with tools like Keploy for testing, new members can experiment and learn how different parts of the system interact. ### How can Keploy help enhance automation in software development? Keploy, known for its focus on test generation and automation, can be a complementary tool when used with AI-driven documentation tools. By generating test cases automatically, it ensures that critical functionalities are validated. ## Top 19 Must-Have VS Code Extensions for Developers in 2025 URL: https://www.swapnoneel.site/blog/top-5-must-use-vs-code-extensions-for-developers-in-2025 Date: 2024-12-19T22:30:33.000Z Summary: These 19 VS Code extensions cover testing, containers, Git history, formatting, HTTP requests, navigation, documentation, and AI assistance. Start with the problems you actually have. VS Code is useful on its own, but [VS Code](https://keploy.io/blog/community/how-to-run-tests-in-visual-studio-code-a-complete-guide) extensions let it meet you where you work. A frontend project needs a quick browser preview. A backend project may need HTTP requests and containers. A large repository needs better navigation and a way to see what changed. That does not mean you should install every extension you find. Each one adds another process, setting, or source of suggestions to your editor. Start with the problems you actually have, then keep the extensions that remove friction without making the workspace noisy. ## 1. Keploy for testing and debugging [Keploy](https://keploy.io/) is the testing-focused choice in this list. It supports [unit](https://keploy.io/blog/community/what-is-unit-testing), integration, and API testing across languages such as Python, JavaScript, TypeScript, Java, PHP, and Go. ![Keploy automated testing VS Code extension](https://wp.keploy.io/wp-content/uploads/2025/07/Keploys-Automated-Integration-Testing.webp) It can record and replay API requests, generate tests, and compare behavior across environments. That is useful when a service already has working traffic but not enough repeatable tests. If you are adding these checks to a [CI/CD](https://keploy.io/blog/community/how-cicd-is-changing-the-future-of-software-development) pipeline, keep the generated cases small enough to diagnose when they fail. Read them before keeping them, especially when captured requests contain private data. ## 2. Docker for container work The Docker extension brings container tasks into the editor. You can inspect images, containers, and volumes without constantly switching to another window. ![Docker extension for VS Code](https://wp.keploy.io/wp-content/uploads/2024/10/Docker-scaled-e1759610158689.png) It becomes most useful when your application already runs in Docker and you need to check logs, rebuild an image, or debug inside a container. The related [Docker comparison](https://keploy.io/blog/community/podman-vs-docker) is useful if you are still deciding which container tool belongs in your workflow. ## 3. GitLens for repository history GitLens puts authorship, blame information, file history, and branch comparisons close to the code you are reading. ![GitLens extension for Git blame and repository history](https://wp.keploy.io/wp-content/uploads/2024/12/gitlens.png) The best use is not looking up who wrote a line so you can complain about it. It is finding the change that introduced a strange condition and reading the surrounding commit. That context can prevent you from deleting a rule that looks unnecessary today. ## 4. Prettier for consistent formatting Prettier formats supported files according to a shared configuration. With format-on-save enabled, the editor applies the same layout each time you save. ![Prettier code formatter extension](https://wp.keploy.io/wp-content/uploads/2024/12/prettier.png) Formatting does not make a bug disappear, but it keeps style debates out of many code reviews. Pair it with ESLint when you want formatting and code-quality checks to have separate jobs. Let the project configuration win over your personal preference. ## 5. Live Server for a quick browser preview Live Server opens a local preview and reloads the page when you save a file. It is handy for small HTML, CSS, and JavaScript projects where setting up a full application server would be unnecessary. ![Live Server local development preview extension](https://wp.keploy.io/wp-content/uploads/2024/12/live-server.png) It will not reproduce every detail of a production deployment, so treat it as a quick feedback loop rather than a complete test environment. That distinction matters when your page depends on a backend, build step, or special headers. ## 6. ESLint for JavaScript and TypeScript checks ESLint reads JavaScript and TypeScript files against rules chosen by your project. It can catch suspicious patterns while you are typing and can report the same problems in continuous integration. ![ESLint static code analysis extension](https://wp.keploy.io/wp-content/uploads/2024/12/eslint.png) The extension is only as helpful as its configuration. Start with rules the team understands, then add stricter checks when the codebase is ready for them. If every line is covered by a warning nobody will fix, the warnings become wallpaper. ## 7. REST Client for requests inside VS Code REST Client lets you keep HTTP requests in `.http` files and run them from the editor. You can inspect JSON or XML responses without copying the request into another application. ![REST Client extension for HTTP requests](https://wp.keploy.io/wp-content/uploads/2024/12/restclient.jpg) This is useful for keeping a small, reviewable set of API examples beside the service code. Put test credentials in environment variables or a local secret store rather than committing them into the request file. ## 8. Path Intellisense for file paths Path Intellisense suggests filenames and folders as you write import statements or HTML links. ![Path Intellisense extension for file path autocompletion](https://wp.keploy.io/wp-content/uploads/2024/12/path-interllisense.png) The benefit shows up in repositories with deeply nested folders, where a path typo can take longer to find than to fix. It saves a few keystrokes, but those small savings add up when you move through a project all day. ## 9. Markdown Preview Enhanced for documentation Markdown Preview Enhanced gives you a live view of Markdown while you edit it. The listed features include syntax highlighting, diagrams, LaTeX, charts, and export options. ![Markdown All in One extension](https://wp.keploy.io/wp-content/uploads/2024/12/markdown-e1759613192640.jpg) Use it when the rendered page matters as much as the source. A heading that looks fine in plain text can wrap badly, hide a broken link, or make a table hard to scan in the preview. ## 10. GitHub Copilot for code suggestions [GitHub Copilot](https://keploy.io/blog/community/cursor-vs-github-copilot) can suggest code and text from the context around your cursor. ![GitHub Copilot AI code completion extension](https://wp.keploy.io/wp-content/uploads/2024/12/github-copilot.jpg) It is most useful for repetitive code, test scaffolding, and drafts that you already know how to review. It can also produce a confident answer that is wrong, insecure, or out of date. Read every accepted suggestion and run the tests that matter; speed is not a substitute for checking the result. ## 11. Bracket Pair Colorization for nested code Bracket Pair Colorization uses matching colors to show which opening and closing brackets belong together. ![Bracket Pair Colorization extension](https://wp.keploy.io/wp-content/uploads/2024/12/bracket.jpg) It helps when a function contains several nested objects, arrays, or callback expressions. The extension does not fix structure for you, but it makes a missing or misplaced bracket easier to spot. ## 12. IntelliCode for ranked suggestions IntelliCode changes the order and context of suggestions in IntelliSense. It is intended to help common patterns appear sooner while you type. ![IntelliCode AI-assisted code completion extension](https://wp.keploy.io/wp-content/uploads/2024/12/intellicode-ai-powered.png) Treat the suggestions as a nudge, not as a decision. If you already use another completion assistant, compare the two before keeping both enabled. More suggestions can make the editor feel busier rather than faster. ## 13. Peacock for separating workspaces Peacock changes the color of a VS Code workspace. That sounds cosmetic until you have several repositories open and nearly run a command in the wrong terminal. ![Peacock workspace color customizer extension](https://wp.keploy.io/wp-content/uploads/2024/12/peacock.png) Assign a different color to projects that are open at the same time. The color becomes a quick visual warning about which folder you are editing. ## 14. Project Manager for switching repositories Project Manager saves workspace entries so you can move between repositories without searching through folders each time. ![Project Manager extension for switching projects](https://wp.keploy.io/wp-content/uploads/2024/12/project-manager.png) It is a good fit when your day involves several codebases. Give entries names you will recognize later, and remove old projects so the list does not become another place to search. ## 15. TODO Highlight for unfinished work TODO Highlight makes markers such as `TODO` and `FIXME` visible in the editor and lets you move between them. ![TODO Highlight extension for code annotations](https://wp.keploy.io/wp-content/uploads/2024/12/todo-highlight.jpg) Use it as a reminder system, not as a substitute for tracking work. A TODO that has no owner or issue link can sit in a file for years. When a marker represents real work, give it enough context that someone can act on it. ## 16. Import Cost for dependency awareness Import Cost displays the size associated with an imported package in JavaScript and TypeScript projects. ![Import Cost extension for package bundle size](https://wp.keploy.io/wp-content/uploads/2024/12/cost-extension.jpg) The number is a prompt to investigate, not an automatic reason to remove a dependency. Check how the package is bundled and whether the import is on a user-facing path before making a change. ## 17. Settings Sync for a consistent setup Settings Sync keeps extensions, themes, and editor settings available across machines through a GitHub account. ![Settings Sync extension for VS Code configurations](https://wp.keploy.io/wp-content/uploads/2024/12/settings-sync-scaled-e1759613437823.png) Sync is convenient, but do not treat a personal settings bundle as a project requirement. Keep team rules in the repository, and check what is being synchronized before including tokens or machine-specific paths. ## 18. Code Spell Checker for names and comments Code Spell Checker catches likely spelling mistakes in identifiers, comments, and documentation. ![Code Spell Checker extension](https://wp.keploy.io/wp-content/uploads/2024/12/Code-Spell-Checker.webp) It is especially helpful when a misspelled variable name has already spread across several files. Add project-specific words to its dictionary rather than ignoring every warning. ## 19. Code Time for activity patterns Code Time records coding activity and presents trends or goals. Some people like seeing how their working sessions change over time; others find the numbers distracting. ![Code Time extension for developer productivity metrics](https://wp.keploy.io/wp-content/uploads/2024/12/code-time.png) My caveat is that time in the editor is not the same thing as useful work. Use these metrics as a personal signal if they help you notice a habit, but do not turn them into a scoreboard for yourself or a team. ## A smaller starting set You do not need all 19 extensions on day one. For a new web project, I would start with Prettier, ESLint, Live Server, and REST Client. Add Docker when the project uses containers, GitLens when repository history becomes important, and a testing extension when you have a repeatable test workflow to support. Also, watch the editor after installing anything new. If startup gets slow or suggestions become noisy, disable extensions one at a time and keep the ones that solve a real problem. The best setup is the one you can explain, maintain, and still enjoy using. ## Common setup questions Open the Extensions view in VS Code, search for an extension, and select Install. Most of the extensions in this list have a free path, but check the extension's own listing for current licensing and optional paid features. Keep extensions updated when the changes fit your project, and disable ones you no longer use. If an AI extension suggests code, review it for correctness, security, and fit with your repository before committing it. ## FAQs ### 1. How do I install VS Code extensions? Open the Extensions view in VS Code, search for your desired extension, and click "Install." ### 2. Are these extensions free? Most are free, though some, like extension, have premium tiers for advanced features. ### 3. Can I use these extensions on other editors? Many are available on other editors, but VS Code integrations provide the best experience. ### 4. Will too many extensions slow down VS Code? Yes, disable unused extensions and monitor performance to keep VS Code running smoothly. ### 5. How do I keep my extensions updated? Enable auto-update in VS Code settings or manually update via the Extensions view. ### 6. Which extensions are best for beginners? Start with Prettier, ESLint, Live Server, and Keploy they’re easy to use and boost productivity immediately. ### 7. Can I use AI extensions safely in professional projects? Yes, AI suggestions should be reviewed, but extensions like IntelliCode can greatly speed up coding tasks. ## Unit Testing vs Integration Testing: A Comprehensive Guide URL: https://www.swapnoneel.site/blog/unit-testing-vs-integration-testing-a-comprehensive-guide Date: 2024-12-17T23:48:38.000Z Summary: Unit tests check one piece of logic; integration tests check how pieces work together. This guide compares their scope, speed, use cases, tools, and a practical testing plan. When you change software, you want an answer to a simple question: did this change break the behavior that was already working? [Software testing](https://keploy.io/blog/community/software-testing-basics) gives you that evidence, while unit tests and integration tests answer the question at different distances. A unit test stays close to one piece of logic. An integration test crosses a boundary between pieces, such as an application and a database. You need both when the risk lives in the code and in the connections around it. ## What unit testing checks [Unit testing](https://keploy.io/blog/community/what-is-unit-testing) exercises a small unit in isolation. A unit is often a function, method, or class, but the useful definition is smaller: it is the piece you can call and check without starting the rest of the application. ![Unit testing process overview](https://wp.keploy.io/wp-content/uploads/2024/12/What-Is-Unit-Testing.webp) The test supplies an input and checks the output or the observable effect. Dependencies such as databases and network clients are usually replaced with fakes, stubs, or mocks so the test stays focused on the unit's own decision. That isolation makes unit tests fast and easier to debug. It also makes them cheap to run while you are editing code. The limitation is just as important: a passing unit test does not prove that the unit is wired correctly to a real database or service. ### A small unit test This example can run with a Python test runner such as PyTest: ```python def add_numbers(a, b): return a + b def test_add_numbers(): assert add_numbers(2, 3) == 5 assert add_numbers(-1, 1) == 0 ``` The test checks two behaviors: ordinary addition and a result that returns to zero. If it fails, you have a short function and a short list of places to inspect. ## What integration testing checks [Integration testing](https://keploy.io/blog/community/integration-testing-a-comprehensive-guide) checks whether separate parts of the application work together. That might mean calling an HTTP route that reads from a database, sending a message to a queue, or passing a value from one module to another. ![Integration testing process overview](https://wp.keploy.io/wp-content/uploads/2024/12/What-Is-Integration-Testing.webp) Because the test crosses a boundary, it needs more setup. You may create a test database, load known data, start a service, or configure a client. The test takes longer than a unit test, but it can catch problems that isolation hides: a wrong column name, a mismatched JSON field, an incorrect serializer, or a connection setting that only fails outside the mock. ### A runnable integration test This small example uses an in-memory SQLite database from Python's standard library. It creates the table, inserts a record, and reads it through the function under test, so it can run without a separate database server. ```python import sqlite3 def get_user(connection, user_id): row = connection.execute( "SELECT name, email FROM users WHERE id = ?", (user_id,), ).fetchone() return row def test_get_user_from_database(): connection = sqlite3.connect(":memory:") connection.execute( "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)" ) connection.execute( "INSERT INTO users (name, email) VALUES (?, ?)", ("John Doe", "johndoe@example.com"), ) connection.commit() user = get_user(connection, 1) assert user == ("John Doe", "johndoe@example.com") connection.close() ``` This test is still small, but it checks the application code against a real database engine. It does not prove that production uses the same configuration, so a larger system may also need tests against its actual database setup. ## How the two types differ Think of unit testing as checking a single conversation and integration testing as checking that the conversation reaches the right person. A unit test can prove that a formatter returns the expected string. An integration test can prove that the route calls the formatter, stores the result, and returns the expected response. Unit tests usually use mocked or in-memory dependencies and finish quickly. Integration tests use more realistic dependencies, need setup and cleanup, and take longer. Unit failures tend to point near the defect; integration failures can require you to inspect the boundary and both sides of it. The difference is about scope, not importance. A unit test is not a cheaper version of an integration test, and an integration test is not automatically a better test. Each one protects a different promise. ## When to write each test Write a unit test when the behavior belongs to one function or module. Calculations, parsing rules, validation, and branching logic are good candidates. Run these tests on every change so they give you quick feedback while the code is fresh in your mind. Write an integration test when the risk is in the connection. Use one for an API and its database, a service and its message broker, or a repository and the storage system it actually uses. Focus on the boundaries that would be expensive to discover through manual debugging. Do not wait until every unit test passes before thinking about integration tests. A unit test can guide local design, while a small integration test can reveal early that the chosen interface does not fit the real dependency. ## Practices that keep tests useful For unit tests, keep the Arrange-Act-Assert shape visible. Arrange the inputs and substitutes, call the unit once, and assert the behavior that matters. Keep each test focused enough that a failure explains what changed. Mock only the dependencies that need isolation. If you mock every object in the call chain, the test may pass while the real objects disagree about a method name or data shape. That is a good signal to add an integration test instead. For integration tests, use an isolated environment and deterministic data. Create the records you need, clean them up after the test, and do not let one test depend on leftovers from another. Add failure cases such as an unavailable service, a timeout, or a missing record when those failures matter to the application. Keep the test setup close to the behavior it protects. Shared fixtures are convenient, but a huge fixture can hide why a test needs a particular record. A little repetition is often easier to maintain than invisible global state. ## Tools and Keploy's place JUnit for Java, PyTest for Python, and [Jest for JavaScript](https://keploy.io/blog/community/migrate-from-jest-to-vitest) are common choices for unit tests. This [JUnit comparison](https://keploy.io/blog/community/testng-vs-junit-performance-ease-of-use-and-flexibility-compared) can help when you are choosing a Java test framework. For integration and API checks, tools such as Postman, Selenium, and Testcontainers can help you exercise the boundary with more realistic dependencies. The original article also highlights Keploy for API integration testing. Its approach records API traffic and turns those interactions into reusable test cases. That can save manual setup when an API already has representative traffic, but you should still remove sensitive values and review each expected response. Keploy can be useful around API boundaries, but it does not remove the need for small unit tests. A captured request can tell you that an endpoint behaves a certain way. It cannot replace a focused test for every calculation inside the endpoint. My caveat is that I would not choose a tool before choosing the behavior I need to protect. A plain test in the framework your team already runs is often a better starting point than a new dashboard full of tests nobody understands. ## A practical testing plan Pick one important workflow. Cover its local rules with unit tests, then add one integration test that crosses its most valuable boundary. Run both in the normal development and CI checks, and add an end-to-end test only when the full user journey adds information that the lower layers cannot provide. When a test fails, first ask which promise it was meant to protect. If the failure message cannot answer that, improve the test before adding more cases. A smaller suite with clear failures will help you more than a large suite that only tells you that something somewhere is red. ## FAQ ### **What is the main difference between unit testing and integration testing?** Unit testing focuses on testing individual components of the application in isolation, whereas integration testing validates the interactions between multiple components to ensure they work together as expected. ### **Why is unit testing faster than integration testing?** Unit tests operate in isolation, often using mocks or stubs for dependencies, which eliminates external system overhead. Integration tests involve real systems like databases or APIs, which increase execution time due to setup and network dependencies. ### **Can unit testing replace integration testing?** No, unit testing cannot replace integration testing. Unit tests verify the correctness of individual components, while integration tests ensure that these components work seamlessly when combined. Both are necessary for robust software testing. ### **How does Keploy assist in integration testing?** Keploy is an open-source platform that simplifies integration testing by automatically generating test cases from API interactions. It reduces the manual effort involved in writing integration tests and ensures seamless validation of API behavior. ### **Should integration tests include real systems or mocks?** Integration tests are most effective when they include real systems, as this mimics actual usage scenarios. However, in certain cases, lightweight mocks may be used to simulate unavailable external systems during testing. ### **How can I ensure integration tests are reliable?** To ensure reliability, use isolated test environments, automate the setup and teardown process, and simulate realistic scenarios. Tools like Keploy can help generate and maintain high-quality integration test cases. ![Thank you graphic for testing guide](https://wp.keploy.io/wp-content/uploads/2024/11/Thank-you.webp) ## Inheritance in Python with Examples URL: https://www.swapnoneel.site/blog/inheritance-in-python Date: Tue, 07 Mar 2023 15:49:19 GMT Summary: Python inheritance lets a class reuse or replace behavior from a parent. This guide covers single, multiple, multilevel, hybrid, and hierarchical inheritance with examples. ## Start with the relationship Inheritance lets one class begin with behavior from another class. The new class is the child or subclass, and the existing class is the parent or base class. ```python class Animal: def eat(self): print("eating") class Dog(Animal): pass dog = Dog() dog.eat() ``` Dog gets eat from Animal, so the last line prints eating even though Dog has no method of its own. Python looks in Dog first, then follows the class's method resolution order until it finds the requested name. That sounds simple, and it is. The design question is harder: should Dog really be treated as an Animal? Inheritance fits an "is a" relationship. A Dog is an Animal. A Dog has a Collar, so a collar would usually be another object stored on the dog, not a parent class. That distinction prevents a lot of awkward hierarchies. ## Single inheritance Single inheritance gives a class one direct parent. It is the easiest form to read because the lookup path has one branch. ![Single inheritance diagram](https://media.geeksforgeeks.org/wp-content/uploads/20200108135809/inheritance11.png) Here, Dog reuses the name setup from Animal and replaces the generic sound: ```python class Animal: def __init__(self, name): self.name = name def make_sound(self): print("A sound comes from the animal.") class Dog(Animal): def make_sound(self): print("Bark!") dog = Dog("Max") print(dog.name) dog.make_sound() ``` Dog does not define **init**, so Python finds Animal.**init** and uses it. Dog does define make_sound, so its method wins over the parent version. The output is: ```text Max Bark! ``` Override a method when the child has a genuinely more specific version of the same behavior. If the child merely needs to add a little work, call the parent implementation with super() rather than copying its body. ## Multiple inheritance Multiple inheritance gives one class more than one direct parent. Small mixins are the least surprising use case: each parent supplies a separate behavior, and the child combines them. ![Multiple inheritance diagram](https://media.geeksforgeeks.org/wp-content/uploads/20200108144424/multiple-inheritance1.png) ```python class Swimmer: def swim(self): print("Swimming") class Runner: def run(self): print("Running") class Triathlete(Swimmer, Runner): pass athlete = Triathlete() athlete.swim() athlete.run() print(Triathlete.__mro__) ``` The class gets swim from Swimmer and run from Runner. The final print shows the order Python searches, ending with object. If both parents define the same method, Swimmer wins in this example because it appears first in the class definition. That does not mean you should pick a parent order at random. The method resolution order is part of the behavior. Also, parent initializers need care. Calling ParentA.**init** and ParentB.**init** manually can work, but it becomes fragile when a third class enters the hierarchy. Cooperative classes use super() and accept compatible arguments so Python can walk the whole MRO once. ## Multilevel inheritance Multilevel inheritance creates a chain. One class extends a parent, and another class extends that child. It is useful when each level describes a real increase in specialization. ![Multilevel inheritance diagram](https://media.geeksforgeeks.org/wp-content/uploads/20200108144705/Multilevel-inheritance1.png) ```python class Animal: def __init__(self, name): self.name = name def show_details(self): print(f"Name: {self.name}") class Dog(Animal): def __init__(self, name, breed): super().__init__(name) self.breed = breed def show_details(self): super().show_details() print(f"Breed: {self.breed}") class GoldenRetriever(Dog): def __init__(self, name, color): super().__init__(name, "Golden Retriever") self.color = color def show_details(self): super().show_details() print(f"Color: {self.color}") dog = GoldenRetriever("Max", "Golden") dog.show_details() ``` The call to GoldenRetriever.show_details travels through all three implementations because each method does its own work and then calls super(). The output is: ```text Name: Max Breed: Golden Retriever Color: Golden ``` Notice that super() does not mean "call my immediate parent by name" in a simple fixed sense. It follows the MRO from the current class. That detail becomes valuable in cooperative multiple inheritance, and it is also why direct calls such as Animal.show_details(self) can make a hierarchy harder to extend. ## Hybrid inheritance and the MRO Hybrid inheritance combines shapes. A common example has two branches that share a base class, followed by a child that inherits from both branches: ![Hybrid inheritance diagram](https://media.geeksforgeeks.org/wp-content/uploads/Hybrid-Inheritance.png) ```python class Person: def __init__(self, name): self.name = name class Student(Person): def study(self): print(f"{self.name} is studying.") class Athlete(Person): def train(self): print(f"{self.name} is training.") class StudentAthlete(Student, Athlete): pass person = StudentAthlete("Mina") person.study() person.train() print(StudentAthlete.__mro__) ``` StudentAthlete can use study and train, and the inherited Person initializer supplies name. The MRO prevents Person from being visited twice in the diamond-shaped path. That is the part you need to understand before adding parent initializers or methods with the same name. If each branch has its own setup, make the constructors cooperate: ```python class Person: def __init__(self, name, **kwargs): super().__init__(**kwargs) self.name = name class Student(Person): def __init__(self, name, subject, **kwargs): super().__init__(name=name, **kwargs) self.subject = subject class Athlete(Person): def __init__(self, name, sport, **kwargs): super().__init__(name=name, **kwargs) self.sport = sport class StudentAthlete(Student, Athlete): def __init__(self, name, subject, sport): super().__init__(name=name, subject=subject, sport=sport) ``` This pattern is more work than the earlier example, but each initializer passes the remaining keyword arguments along. A mismatch in the signatures will raise TypeError, which is much easier to fix than silently skipping half of an object's state. ## Hierarchical inheritance Hierarchical inheritance has several children sharing one parent. The parent holds the common setup, while each child adds or replaces the part that differs. ![Hierarchical inheritance diagram](https://media.geeksforgeeks.org/wp-content/uploads/20200108144949/Hierarchical-inheritance1.png) ```python class Animal: def __init__(self, name): self.name = name def show_details(self): print(f"Name: {self.name}") class Dog(Animal): def __init__(self, name, breed): super().__init__(name) self.breed = breed def show_details(self): super().show_details() print(f"Breed: {self.breed}") class Cat(Animal): def __init__(self, name, color): super().__init__(name) self.color = color def show_details(self): super().show_details() print(f"Color: {self.color}") Dog("Max", "Golden Retriever").show_details() Cat("Luna", "Black").show_details() ``` Dog and Cat both inherit the name handling, but they do not need to share their breed and color fields. This shape is often cleaner than making one large Animal class full of conditionals about which species is currently active. ## Where inheritance stops helping Inheritance is useful when the child can be passed to code that expects the parent. It is less useful when several classes merely share a few lines. In that situation, a helper function, a small mixin, or composition can keep the relationship honest. My against-interest judgment is that inheritance feels cheap at first and expensive after several layers. A subclass can depend on a parent method, an attribute name, an initializer order, and an MRO detail without making those dependencies visible at the call site. I would rather repeat a small, clear function than force unrelated classes into the same family. Use inheritance for a real type relationship, then stop the hierarchy before it becomes archaeology. ![Thank you lettering graphic](https://static.vecteezy.com/system/resources/previews/017/125/080/large_2x/thank-you-design-lettering-free-vector.jpg) ## Magic Methods in Python Explained URL: https://www.swapnoneel.site/blog/magic-methods-in-python Date: Wed, 22 Feb 2023 14:42:38 GMT Summary: Magic, or dunder, methods let Python objects respond to built-in syntax such as printing, len(), construction, and function calls. ## What magic methods do Magic methods are special methods whose names start and end with two underscores. Python calls them for ordinary operations such as creating an object, printing it, asking for its length, or calling it like a function. You may also hear them called dunder methods. You usually do not call these methods directly. You write the method that matches the behavior you want, then use normal Python syntax and let Python make the call. The useful way to learn them is to start with the syntax you want your object to support. If `print(playlist)` should be readable, look at `__str__`. If `len(playlist)` should mean something, look at `__len__`. You do not need to memorise a catalogue of every dunder method before writing a class. ## **init** sets the initial state Python calls `__init__` after it creates a new instance. Put the initial instance attributes there. ```python class Playlist: def __init__(self, songs): self.songs = list(songs) playlist = Playlist(["Intro", "Signal"]) print(playlist.songs) ``` The `list(songs)` call makes a new list for the object. The initializer prepares the object; it does not return the object itself. ## **str** and **repr** describe an object `str(value)` and `print(value)` prefer `__str__`, which should be readable to a person. `repr(value)` is meant for debugging and should contain enough detail to identify the value clearly. ```python class Playlist: def __init__(self, songs): self.songs = list(songs) def __str__(self): return ", ".join(self.songs) def __repr__(self): return f"Playlist({self.songs!r})" playlist = Playlist(["Intro", "Signal"]) print(playlist) print(repr(playlist)) ``` The readable form is `Intro, Signal`. The representation is `Playlist(['Intro', 'Signal'])`. A `__repr__` result does not have to be executable, but showing the important state makes debugging much less frustrating. ## **len** supports len() Define `__len__` when your object has a meaningful size. Python expects the method to return a non-negative integer. ```python class Playlist: def __init__(self, songs): self.songs = list(songs) def __len__(self): return len(self.songs) playlist = Playlist(["Intro", "Signal"]) print(len(playlist)) ``` The call to `len(playlist)` is Python's friendly syntax for asking the object for `playlist.__len__()`. The same idea applies to comparisons. If two objects represent the same value, `__eq__` can define what `first == second` means: ```python class Song: def __init__(self, title): self.title = title def __eq__(self, other): if not isinstance(other, Song): return NotImplemented return self.title == other.title print(Song("Signal") == Song("Signal")) ``` Without `__eq__`, two separate `Song` objects compare by identity, even when their attributes match. Returning `NotImplemented` for an unrelated type lets Python handle that comparison instead of pretending every object is comparable to a `Song`. ## **call** makes an object callable Functions are objects too. A regular object can act like a function when its class defines `__call__`. ```python class Multiplier: def __init__(self, factor): self.factor = factor def __call__(self, value): return value * self.factor double = Multiplier(2) print(double(5)) ``` `double(5)` calls `double.__call__(5)`, so the object remembers its factor between calls. This pattern is useful when a callable needs configuration or state. Another common dunder is `__iter__`, which lets an object participate in a `for` loop. For a playlist, iteration should expose the songs in the same order a caller expects: ```python class Playlist: def __init__(self, songs): self.songs = list(songs) def __iter__(self): return iter(self.songs) playlist = Playlist(["Intro", "Signal"]) for song in playlist: print(song) ``` These methods make an object fit a familiar Python protocol. That is the real benefit: callers can use normal language features instead of learning a private API for every class. My caveat is to add a magic method only when normal syntax becomes clearer. A custom `__str__` often helps immediately. A pile of clever dunders can make a class feel like it is fighting Python instead of working with it. Keep return types and failure behavior unsurprising: `__len__` should return a non-negative integer, `__str__` should return a string, and an operator should reject unsupported values clearly. ![Painted thank you label graphic](https://img.freepik.com/free-vector/painted-thank-you-label-template_23-2148689616.jpg?w=1380&t=st=1677075508~exp=1677076108~hmac=168e84f6c0a2f5c63b505e2ac25f9d6200ecf461d2fea92d9e8526809c011186) ## Overriding and Overloading in Python Explained URL: https://www.swapnoneel.site/blog/overriding-overloading-in-python Date: Tue, 21 Feb 2023 11:34:18 GMT Summary: Method overriding replaces inherited behavior, while operator overloading gives expressions such as + a meaning for your own objects. Inheritance becomes useful when the calling code can ask different objects to do the same job. A `Circle` and a `Rectangle` can both answer `area()`, even though the calculation is different. The caller keeps one method name, while each class supplies the behavior that belongs to it. There are two different ideas in this post. Method overriding changes inherited behavior. Operator overloading gives a familiar operator, such as `+`, a meaning for your own class. They are related because both depend on Python choosing a method at runtime, but they solve different problems. ## Replacing inherited behavior When a child class defines a method with the same name as a method in its parent, Python finds the child implementation first. That is method overriding. Here is a complete example with one common interface: ```python class Shape: def area(self): raise NotImplementedError("Each shape must define area()") class Circle(Shape): def __init__(self, radius): self.radius = radius def area(self): return 3.14159 * self.radius ** 2 class Rectangle(Shape): def __init__(self, width, height): self.width = width self.height = height def area(self): return self.width * self.height shapes = [Circle(2), Rectangle(3, 4)] for shape in shapes: print(shape.area()) ``` The output is approximately `12.56636` followed by `12`. The loop does not need an `isinstance()` check because every object follows the same `area()` contract. That is the useful part of overriding: the caller can stay ignorant of the concrete class. Python does not enforce an identical signature or return type when you override a method. Your design still should. If every shape is expected to answer `area()` without arguments, adding a required argument in one child breaks the promise made by the parent. Inheritance does not remove the need to keep an interface consistent. ## Keeping part of the parent behavior Sometimes the child needs to add a detail rather than replace everything. `super()` calls the next implementation in Python's method resolution order, which is usually the parent method in a simple hierarchy. For example, the parent can provide a general description: ```python class Shape: def describe(self): return "This object is a shape." class Circle(Shape): def describe(self): parent_description = super().describe() return f"{parent_description} It is specifically a circle." print(Circle().describe()) ``` The output contains both sentences. My caveat is that `super()` is easy to add mechanically and harder to justify mechanically. Use it when the parent's work remains part of the child behavior. If the child is meant to replace that behavior completely, calling `super()` only makes the result harder to follow. ## Giving operators a meaning Operator overloading lets a class define what an operator means for its instances. Python translates an expression such as `p1 + p2` into a special method call, roughly `p1.__add__(p2)`. These are called dunder methods because their names begin and end with two underscores. ### A point that can be added Suppose a point has an `x` coordinate and a `y` coordinate. Adding two points coordinate by coordinate is a meaning a reader can understand, so `+` is a reasonable fit: ```python class Point: def __init__(self, x, y): self.x = x self.y = y def __add__(self, other): if not isinstance(other, Point): return NotImplemented return Point(self.x + other.x, self.y + other.y) p1 = Point(1, 2) p2 = Point(3, 4) p3 = p1 + p2 print(p3.x, p3.y) ``` The output is `4 6`. Returning `NotImplemented` tells Python that this operand type is unsupported and gives the other operand a chance to handle the operation. Returning `None` would be different: the expression would appear to work and leave you with an unusable result. The same idea maps other operators to special methods: `-` calls `__sub__()`, `*` calls `__mul__()`, `<` calls `__lt__()`, and `==` calls `__eq__()`. There is one naming trap. Python does not support traditional method overloading where several methods share one name and differ only by parameter types. If you define `load()` twice, the second definition replaces the first. Default arguments or `*args` can support different call shapes, but a single clear method is usually easier to test. So the judgment is fairly simple. Override a parent method when a child needs a different implementation of the same contract. Overload an operator when the resulting expression reads naturally and rejects unsupported types clearly. If the expression needs a paragraph of explanation before it makes sense, a named method is probably the better design. ![Thank you banner graphic](https://www.incimages.com/uploaded_files/image/1920x1080/getty_469566889_105923.jpg) ## Class Methods in Python with Examples URL: https://www.swapnoneel.site/blog/class-methods-in-python Date: Mon, 20 Feb 2023 11:59:17 GMT Summary: Python class methods receive the class as cls, so they can build objects from alternate inputs and manage class-level behavior without an existing instance. ## Start with the question of who owns the work Before talking about decorators, ask one plain question: what does this method need to know? An instance method needs one particular object, so Python gives it that object as self. A class method needs the class, so Python gives it the class as cls. A static method needs neither. That is the whole split, and most of the confusing explanations become easier once you keep that ownership question in view. Here is a small class with one method that belongs to an object and another that belongs to the class: ```python class User: def __init__(self, name, role): self.name = name self.role = role def describe(self): return f"{self.name} is a {self.role}." @classmethod def guest(cls): return cls("Guest", "reader") user = User.guest() print(user.describe()) ``` describe() cannot do anything useful until a User exists, because it reads self.name and self.role. guest() has no existing user to inspect. It is a recipe for making one, so Python calls it with User as cls. The output is: ```text Guest is a reader. ``` That is why User.guest() works before you have an instance. The decorator changes how the function is bound when you access it through the class. ## What the decorator changes Without @classmethod, this method is an ordinary function sitting in the class body. If you call it through an instance, Python supplies that instance as the first argument. With @classmethod, Python stores a class-bound method instead and supplies the class. You can see the practical difference by trying to use an instance method as a factory: ```python class Ticket: def __init__(self, number): self.number = number def from_text(self, text): return Ticket(int(text)) # Ticket.from_text("42") # TypeError: self is missing ticket = Ticket(1) print(ticket.from_text("42").number) ``` The commented call fails because from_text expects self. You have to create a meaningless Ticket(1) before you can use it. The class method version expresses the intent directly: ```python class Ticket: def __init__(self, number): self.number = number @classmethod def from_text(cls, text): return cls(int(text)) ticket = Ticket.from_text("42") print(ticket.number) ``` Now parsing and construction have a name of their own, and callers do not need to know how the text is converted. ## Alternative constructors are the sweet spot The usual reason to write a class method is that one type can arrive in several input formats. Keep the normal **init** for the canonical arguments, then add named entry points for other formats. ```python class Person: def __init__(self, name, age): self.name = name self.age = age @classmethod def from_string(cls, text): name, age_text = text.split(",", maxsplit=1) return cls(name.strip(), int(age_text)) @classmethod def child(cls, name): return cls(name, 0) print(Person.from_string("John Doe, 30").age) print(Person.child("Mina").name) ``` The constructor still owns the actual object setup. The class methods only translate input into the arguments that the constructor expects. If you later add validation in **init**, both alternate paths get it automatically. There is one detail here that is easy to miss. Use cls(...), not Person(...), inside a class method: ```python class Employee(Person): pass employee = Employee.from_string("Ravi, 28") print(type(employee).__name__) ``` The output is Employee. Python passes the class used for the call as cls, so the method stays friendly to subclasses. Hard-code Person(...) and you quietly throw that behavior away. Bad input still fails. A string without a comma raises ValueError during unpacking, and text such as "Ravi, twenty" raises ValueError when int() runs. That is not a class method problem. It is the parser telling you that the input is not in the format it promised to accept. If the format comes from users or a file, catch those errors at the boundary and report the bad input there. ## Class methods versus static methods A static method is useful when a function is conceptually grouped with a class but does not need either the object or the class: ```python class Person: @staticmethod def valid_age(age): return isinstance(age, int) and age >= 0 print(Person.valid_age(30)) ``` This could be a module-level function too. Keeping it on Person is reasonable if the rule is meaningful only in that small namespace. A class method is the better choice when the class itself matters, especially for factories that must preserve subclasses. A regular method is the right choice when the answer depends on one object's state. My against-interest judgment is that class methods are easy to overuse. If a function does not construct an object or work with class-wide state, putting @classmethod on it adds ceremony and makes the reader wonder what cls is for. Start from the data the method needs. The first argument usually tells you which kind of method you actually have. ![Thank you label illustration](https://img.freepik.com/free-vector/painted-thank-you-label-template_23-2148689616.jpg?w=1380&t=st=1676893691~exp=1676894291~hmac=9f0960bb4730c2bbfdc9558840a6a8ed356377041f759a66392bdfff8f0612f2) ## Class Variables vs Instance Variables in Python URL: https://www.swapnoneel.site/blog/class-variables-vs-instance-variables Date: Sun, 19 Feb 2023 11:38:55 GMT Summary: Class variables live on the class and are shared by default, while instance variables belong to one object. Learn how lookup and mutable defaults affect Python classes. ## The lookup happens in two places Take account.owner. Python first asks the account object whether it has an owner attribute. If it does not, Python checks the class and then the classes above it. That small lookup rule is why a class attribute can appear to belong to every instance. The value has not been copied into each object, though. An instance variable is stored on one object. A class variable is stored on the class and is shared by every object that reads it. Once you see where the value lives, the rest follows. ## Instance variables describe one object Put per-object state on self, usually in **init**. Each call to a class creates a separate object, and each object gets its own attribute dictionary. ```python class User: def __init__(self, name, points): self.name = name self.points = points def summary(self): return f"{self.name}: {self.points} points" first = User("John", 12) second = User("Jane", 7) first.points += 5 print(first.summary()) print(second.summary()) ``` The output is: ```text John: 17 points Jane: 7 points ``` first.points += 5 changes the attribute on first. It has no route to second.points. Both objects share the summary method through the class, but their data is separate. You can inspect that storage directly while learning: ```python print(first.__dict__) print(second.__dict__) ``` Each dictionary contains its own name and points. This is a useful debugging trick, but do not build a design around **dict**; some Python objects use **slots** and do not have one. ## Class variables describe the class Put shared data in the class body. A count is a good example because there should be one count for all User objects. ```python class User: total_users = 0 def __init__(self, name): self.name = name type(self).total_users += 1 first = User("John") second = User("Jane") print(User.total_users) print(first.total_users) print(second.total_users) ``` All three prints show 2. The first lookup finds total_users on User. The other two lookups fail to find it on the individual objects, then find it on the class. type(self).total_users is a deliberate choice. It lets a subclass keep its own count instead of always updating the User count. If you want one global count for the whole family, write User.total_users += 1 and make that ownership explicit. Also, prefer User.total_users when reading class-owned data. first.total_users is legal, but it hides the fact that the value is shared. ## Assignment can hide the class value This is the part that catches people: ```python class User: role = "reader" first = User() second = User() first.role = "admin" print(first.role) print(second.role) print(User.role) ``` The output is: ```text admin reader reader ``` The assignment did not update User.role. It created a new role attribute on first, so that one object now shadows the class value. Delete first.role and lookup falls back to User.role again: ```python del first.role print(first.role) ``` This is why class attributes are fine for defaults that instances may override, but they are a poor substitute for a shared setting that callers can casually shadow. ## Mutable class variables share one object Numbers make sharing look harmless. Lists expose the trap immediately: ```python class Team: members = [] def add_member(self, name): self.members.append(name) red = Team() blue = Team() red.add_member("Asha") print(red.members) print(blue.members) ``` Both lines print ['Asha'] because red.members and blue.members found the same list on Team, and append() mutated that list in place. If each team needs its own collection, create it in **init**: ```python class Team: def __init__(self): self.members = [] def add_member(self, name): self.members.append(name) ``` The class-level list is not always wrong. A shared immutable default, or a deliberately shared registry, can be exactly what you need. The failure comes from forgetting that a mutable value is one shared object until you create separate copies. ## My default choice For ordinary application state, I choose instance variables first. They make ownership obvious, prevent accidental cross-object changes, and fit the way most objects are used. I reach for class variables when the value genuinely belongs to the class: a constant, a shared configuration default, or a counter that the class owns. So the practical rule is simple: put per-object data on self, and make shared data visibly class-owned. Be extra suspicious of class-level lists and dictionaries. But that's just me, and your workflow might be different. ![Thank you graphic for class vs instance variables blog](https://img.freepik.com/free-vector/painted-thank-you-label-template_23-2148689616.jpg?w=1380&t=st=1676893691~exp=1676894291~hmac=9f0960bb4730c2bbfdc9558840a6a8ed356377041f759a66392bdfff8f0612f2) ## Getters and Setters in Python Explained URL: https://www.swapnoneel.site/blog/getters-and-setters-in-python Date: Sat, 18 Feb 2023 13:32:09 GMT Summary: Python property() lets a class validate or compute an attribute without making callers use explicit getter and setter methods. ## Keep the public name simple Suppose a Person object stores an age. You want callers to write person.age, not person.get_age() every time they read it. At first, a plain attribute is enough. The trouble begins when a caller assigns -4 or the string "old", and your class has no place to reject it. A property keeps the pleasant attribute syntax while running Python code on reads and writes. It is a useful boundary, but it is not a magic private field. Python still trusts you to respect the interface you chose. ## A property starts as a getter The property decorator turns a method into an attribute-like read: ```python class Person: def __init__(self, age): self._age = age @property def age(self): return self._age person = Person(30) print(person.age) ``` The output is 30. Python sees person.age, calls the age method, and returns the value from \_age. The leading underscore is a convention that says the storage is for internal use. It does not stop someone from writing person.\_age = -4. Because the property has no setter yet, this works: ```python print(person.age) ``` But person.age = 31 raises AttributeError. A read-only property is useful for a value that the object calculates or exposes without allowing outside code to replace it. ## Add validation with a setter Decorate a second method with age.setter. The method receives the value on the right side of the assignment: ```python class Person: def __init__(self, age): self.age = age @property def age(self): return self._age @age.setter def age(self, value): if not isinstance(value, int): raise TypeError("age must be an integer") if value < 0: raise ValueError("age cannot be negative") self._age = value person = Person(30) person.age = 31 print(person.age) ``` The initializer assigns to self.age instead of self.\_age on purpose. That sends the first value through the same validation as every later assignment. Person(-1) raises ValueError, and Person("thirty") raises TypeError before an invalid object can escape. The setter does not have to store the value unchanged. It can normalize it, convert units, or reject a value that violates the class's rules: ```python class Temperature: def __init__(self, celsius): self.celsius = celsius @property def celsius(self): return self._celsius @celsius.setter def celsius(self, value): if value < -273.15: raise ValueError("temperature is below absolute zero") self._celsius = float(value) @property def fahrenheit(self): return self.celsius * 9 / 5 + 32 temperature = Temperature(20) print(temperature.fahrenheit) ``` fahrenheit is computed from celsius, so there is no second piece of state to keep in sync. If you stored both values, every update would create another chance for them to disagree. ## The recursion trap Inside a property getter, return the backing attribute, not the property itself. Inside the setter, assign to \_age, not age: ```python class BrokenPerson: @property def age(self): return self.age @age.setter def age(self, value): self.age = value ``` Reading age calls the getter, which reads age again, which calls the getter again until Python raises RecursionError. Assigning to age creates the same loop in the setter. The private-looking backing name prevents that accidental self-call. ## Do you need a property at all Usually, no. A plain public attribute is a good starting point when it has no validation or calculation behind it. You can replace that attribute with a property later without changing code that reads person.age or assigns to it. That is one of the nicest parts of the pattern. Do not create a getter and setter that only return and assign the same value for the sake of ceremony. In Python, properties earn their place when a read needs a calculation, a write needs a rule, or the stored representation should stay separate from the public name. My caveat is to keep the public name stable and the property small. If a getter performs a database query or a setter triggers half the application, attribute syntax can hide a surprisingly expensive operation. For simple validation and computed values, property is a good fit. For larger actions, an explicit method tells the reader more honestly what a call will do. ![Thank you banner graphic](https://etiquettejulie.com/wp-content/uploads/2017/01/thank-you-from-christian-vision-alliance.jpg) ## Constructors and Decorators in Python with Examples URL: https://www.swapnoneel.site/blog/constructors-and-decorators-in-python Date: Fri, 17 Feb 2023 15:16:04 GMT Summary: Python creates an object before __init__ initializes it, while decorators wrap functions to add behavior. This guide shows both ideas with small examples. ## A class call has two stages When you write Details("Crab", "Crustaceans"), Python does more than run a normal function. It creates an object, then initializes that object with the arguments you passed. The method that creates the object is **new**. The method that prepares its attributes is **init**. People often call **init** the constructor, and that shorthand is fine for everyday work, but the distinction matters when you need to control object creation itself. For most classes, you only write **init**: ```python class Details: def __init__(self, animal, group): self.animal = animal self.group = group details = Details("Crab", "Crustaceans") print(details.animal, "belongs to the", details.group, "group.") ``` self is the newly created object. The assignments attach animal and group to that object, so another Details instance can store different values. The output is: ```text Crab belongs to the Crustaceans group. ``` If you leave out **init**, Python can still create instances when no setup is required. Add an initializer when the object needs a known starting state: ```python class Counter: def __init__(self): self.value = 0 def increment(self): self.value += 1 counter = Counter() counter.increment() print(counter.value) ``` The output is 1. A common mistake is returning a value from **init**. It must return None; its job is to configure self, not replace it. return self and return 5 both raise TypeError when Python calls the class. ## When new matters You can define **new** when object creation needs special rules, such as returning an existing object or creating an immutable value. That is advanced territory. If all you need is to copy arguments into attributes, **init** is the right place. This version makes the order visible: ```python class Example: def __new__(cls, value): print("creating") return super().__new__(cls) def __init__(self, value): print("initializing") self.value = value example = Example(10) print(example.value) ``` The lines print creating, initializing, and 10, in that order. If **new** returns an object that is not an instance of cls, Python will not continue with the usual **init** call. That is one reason not to override it casually. ## A decorator replaces the name with a callable result Now switch from objects to functions. A decorator is a callable that receives a function and returns something that will be used in its place. The @ syntax is just a readable spelling of that assignment. ```python def show_call(func): def wrapper(): print("before") result = func() print("after") return result return wrapper @show_call def greet(): print("hello") return 42 print(greet()) ``` Python reads the decoration roughly like this: ```python def greet(): print("hello") return 42 greet = show_call(greet) ``` After that assignment, the name greet refers to wrapper. Calling greet() prints before, then hello, then after, and finally 42. The original function still runs because the wrapper calls func(). The final return result is easy to forget. If you remove it, the log still appears, but print(greet()) prints None. That kind of bug feels strange when the function body clearly returns a value, because the wrapper has quietly swallowed it. ## A decorator that accepts real arguments A wrapper with no parameters only works for a function with no arguments. In normal code, use \*args and \*\*kwargs so the wrapper can pass along positional and keyword arguments. ```python from functools import wraps def log_function_call(func): @wraps(func) def wrapper(*args, **kwargs): print(f"calling {func.__name__}") result = func(*args, **kwargs) print(f"{func.__name__} returned {result}") return result return wrapper @log_function_call def add(first, second): return first + second print(add(2, second=3)) ``` wraps is worth keeping. Without it, add.**name** would be wrapper, and tools that inspect the function would see the wrapper's metadata instead of add's. This is not just cosmetic when a framework uses names, signatures, or docstrings. Exceptions travel through the wrapper too. If add("2", 3) runs, the addition raises TypeError, the second log line is skipped, and the error reaches the caller. Add a try and finally only when you have a real reason to log failures or clean up resources. ## Where each tool earns its place Use **init** for ordinary object setup. Use **new** only when the act of creating the object needs custom behavior. Use a decorator when the same surrounding behavior belongs around several functions, such as logging or permission checks. My caveat is that decorators hide a call. A tiny @ line can change arguments, errors, metadata, and return values while leaving the function body untouched. When a decorated function behaves oddly, inspect the decorator before blaming the function. I like decorators, but I trust them only when the wrapper is short enough to read in one sitting. ![Thank you card maker graphic](https://cdn.pizap.com/pizapfiles/images/thank_you_card_maker_app01.jpg) ## Introduction to OOPs in Python URL: https://www.swapnoneel.site/blog/introduction-to-oops Date: Thu, 16 Feb 2023 17:58:21 GMT Summary: Object-oriented programming groups data and behavior into classes and objects. This Python guide explains abstraction, encapsulation, inheritance, and polymorphism without treating OOP as a rule. ## What object-oriented programming tries to solve Procedural code starts with actions: read a value, transform it, then save or print the result. That style is perfectly fine for a script that imports a file once. As the program grows, though, the data and the functions allowed to change it can end up scattered across many modules. Object-oriented programming, or OOP, gives related state and behavior a home. A class describes a kind of object. An object is one concrete value made from that class. You are not required to use classes for every problem, and forcing a tiny script into a hierarchy is a quick way to make simple code feel strange. Start with a class that keeps a person's data beside an operation that uses it: ```python class Person: def __init__(self, name): self.name = name def greet(self): return f"Hello, I am {self.name}." first = Person("Ryan") second = Person("Mina") print(first.greet()) print(second.greet()) ``` Person is the class. first and second are objects, also called instances. The class supplies the greet method, while each object stores its own name. The output is: ```text Hello, I am Ryan. Hello, I am Mina. ``` That relationship is the foundation. The four words usually connected with OOP describe different ways of using it. ## Abstraction hides the steps you do not need Abstraction means exposing an operation while keeping its internal steps out of the caller's way. When you call first.greet(), you do not need to rebuild the string or know where name is stored. The method gives you the operation you need. The same idea exists in ordinary functions. A function that loads and validates a configuration file is an abstraction even if the program contains no class. The [OOP concepts overview](https://stackify.com/oops-concepts-in-java/) describes this as hiding complexity behind a simpler interface. Good abstraction has a limit. If the method name is vague, or if it secretly opens files, makes network requests, and changes global state, the caller has a harder time predicting what will happen. Hide the steps that are implementation details, but keep the public action honest. ## Encapsulation keeps rules near the data Encapsulation means putting state and the operations that protect it in the same place. Python does not enforce private fields in the same way as some languages. A leading underscore is a convention, not a locked door, but it tells readers which attribute the class owns internally. ```python class Wallet: def __init__(self, amount=0): if amount < 0: raise ValueError("amount cannot be negative") self._balance = amount def deposit(self, amount): if amount <= 0: raise ValueError("deposit must be positive") self._balance += amount def spend(self, amount): if amount <= 0 or amount > self._balance: raise ValueError("invalid spending amount") self._balance -= amount @property def balance(self): return self._balance wallet = Wallet(20) wallet.deposit(5) wallet.spend(8) print(wallet.balance) ``` The output is 17. The class owns the rules for changing the balance, so callers do not have to remember every check. Someone can still write wallet.\_balance = -100, because Python trusts convention, but ordinary code has a clear public path. This is where encapsulation pays off. If the storage changes from a number to another representation later, the deposit, spend, and balance interface can stay the same. ## Inheritance describes a narrower type Inheritance lets a new class reuse or replace behavior from an existing class: ```python class Animal: def __init__(self, name): self.name = name def speak(self): return "Some sound" class Dog(Animal): def speak(self): return "Bark" dog = Dog("Max") print(dog.name) print(dog.speak()) ``` Dog gets the name setup from Animal and supplies a more specific speak method. This is a reasonable relationship because a dog is an animal. A Dog has a Collar, though, so a collar would usually be stored as another object rather than added as a parent class. Inheritance is useful when code genuinely expects the parent type. It becomes awkward when the only shared feature is a few lines of implementation. A helper function or composition can be clearer in that case. ## Polymorphism lets the caller ignore the concrete type Polymorphism means different objects can respond to the same operation in their own way. Python often handles this through duck typing: the function asks for behavior instead of checking a long list of class names. ```python class Dog: def speak(self): return "Bark" class Cat: def speak(self): return "Meow" def announce(animal): print(animal.speak()) announce(Dog()) announce(Cat()) ``` announce does not need separate branches for Dog and Cat. It only needs an object with a speak method. Pass an object without speak and Python raises AttributeError at the call, which is a useful, direct failure. If you need a friendlier error, validate the interface before doing work, but do not add type checks just to make the code look formal. Python's built-in types use the same idea all over the place. Anything with a **len** method can work with len(), and anything iterable can work in a for loop. The class name matters less than the behavior the operation requires. ## OOP is a tool, not a rule Classes help when state and behavior belong together, when several objects share a clear interface, or when a type has rules that should live in one place. A plain function and a dictionary can be better for a one-off transformation. My honest view is that OOP is easiest to understand after you stop treating its four labels as a checklist. Start with the data and the operations. If they naturally belong together, make a class. If a class would only wrap one function and a couple of values, skip it. The design should make the next change easier, not earn points for containing more objects. ![Thank you image](https://iag.me/assets/thank-you.jpg.webp) ## Best Frameworks for Web Development Compared URL: https://www.swapnoneel.site/blog/best-frameworks-to-use-for-web-development Date: 2023-07-28T15:04:47.555Z Summary: React, Vue, Angular, Express, Solid, Next, and Svelte solve different web problems. Compare their trade-offs and choose a framework without chasing hype. You can lose more time choosing a web framework than writing the first version of the site. A search for the "best" option gives you a pile of rankings, benchmark screenshots, and very confident opinions. None of that tells you what your project actually needs. So start with the boring question: what has to happen when someone visits the page? Maybe the server returns a document and a few assets. Maybe the browser keeps updating a dashboard as the user clicks around. Maybe the application needs an API, authentication, and a team-wide way to keep hundreds of files organized. A framework is a set of decisions around those jobs. It may give you components, routing, data loading, project folders, build tools, or server code. A library usually solves one part and leaves more of the surrounding decisions to you. That distinction matters here because React and SolidJS are primarily interface libraries, while Next.js and Angular provide a wider application structure. Express.js is a Node server framework, so it belongs on the server side of the conversation. ## The questions that narrow the choice Do not begin with popularity. Begin with the shape of the work. For a personal landing page, a framework may be unnecessary. A small amount of HTML and CSS can be easier to deploy and easier to understand six months later. For a dashboard with filters, shared state, and several screens, components and routing start paying for themselves. Now ask where the first HTML should come from. A browser-rendered application can load a small shell and build the page with JavaScript. A server-rendered application can send useful HTML in the first response, then add browser behavior afterward. Content-heavy pages, documentation, and stores often care about that first response more than a private admin screen does. Also ask who will maintain the decisions. A flexible stack lets an experienced team choose exactly what it wants. It gives a new team more opportunities to choose five different patterns for the same problem. An opinionated framework can feel restrictive on day one and calming on day one hundred. My short checklist is simple: 1. Is this mostly static content, a browser application, a server, or a mixture? 2. Do you need server rendering or an API built into the same project? 3. How much structure will make the next change easier? 4. Does the team already know one of these tools well? With that in mind, the names below stop being a popularity contest. ## React React is a JavaScript library for building user interfaces. Its central idea is the component: a piece of markup and behavior that you can reuse inside a larger page. That sounds small, but it changes how you work. A product card can receive a product as a prop, render its title and price, and appear in several screens without copying the markup. A form can keep its input state in one place. A page can be assembled from those smaller parts. ![React framework logo](https://ms314006.github.io/static/b7a8f321b0bbc07ca9b9d22a7a505ed5/97b31/React.jpg) React is a strong fit for interactive applications and shared component libraries. It also has a large collection of surrounding tools, which means unusual problems often have several existing solutions. That same freedom is the part I would warn a beginner about. React does not, by itself, choose your router, data-fetching approach, form library, or folder layout. Two React projects can feel like different ecosystems. Pick it when you want that room and are willing to make the decisions. If you want one official path from page to production, React alone will leave you with homework. ## Vue.js Vue.js is a front-end framework that can sit inside an existing page or support a complete application. Its single-file components keep the template, script, and styles close together, which makes the first example easy to follow. ![Vue.js framework logo](https://segwitz.com/wp-content/uploads/2021/06/vuejs-development-malaysia.jpeg) Vue is a good choice when you want component-based development without a huge amount of ceremony. You can add a small interactive widget to an existing page, then use the same component model for a larger application later. The trade-off is ecosystem size. React has more packages, tutorials, and answers for odd edge cases. Vue still covers the usual work, but you may need to make more of the solution yourself when the problem gets unusual. For a small project where I want to scan the code quickly, I would choose Vue before React. That is a preference, not a law of nature. ## Angular.js Angular is a full front-end framework maintained by Google. It gives you a defined way to write templates, inject services, configure routes, and organize an application. TypeScript is part of the normal setup, so types arrive with the rest of the framework rather than as an optional extra. ![Angular framework logo](https://www.searchenginejournal.com/wp-content/uploads/2019/04/the-seo-guide-to-angular.png) That structure suits large teams that want similar patterns across the codebase. Dependency injection gives services a clear place to live, and the project conventions make it easier to find the expected home for a route or feature. Angular asks you to learn more before the first feature feels comfortable. TypeScript, decorators, templates, services, and the application structure arrive together. I would not choose it for a small page unless the team already works in Angular. For a large application with an Angular team, the rules are the reason to choose it. ## Express.js Express.js runs on Node.js and handles server-side work. A request enters the server, passes through middleware, reaches a route handler, and leaves as a response. That makes Express useful for APIs, small web servers, and the backend behind a React or Vue application. ![Express.js framework logo](https://miro.medium.com/v2/resize:fit:805/0*m1VOQP0FtcQufLgw.png) Express is intentionally small. You add the middleware you need for JSON parsing, authentication, logging, or database access instead of receiving a complete application structure on day one. That flexibility can turn into a pile of decisions. Express will not choose your folder layout or stop every route from becoming a giant function. Pick it when you want a thin Node server and are comfortable designing the rest. Also, do not call it a front-end framework just because it appears in the same web stack. ## Solid.js SolidJS is a JavaScript library for building interfaces with fine-grained reactivity. When a piece of state changes, Solid can update the part of the page that reads that state instead of rerunning a whole component tree in the same way a virtual-DOM approach does. ![SolidJS framework logo](https://www.solidjs.com/og.jpg) Solid's model is attractive when you care about small updates and want to write components with familiar JavaScript and JSX. The price is a smaller ecosystem. A React answer that appears in the first search result may require more reading and experimentation in Solid. I like the model, but I would not make Solid the default recommendation for a beginner who needs the biggest pool of examples and packages. Choose it when the update model or the authoring style solves a real problem, not because a benchmark screenshot looks nice. ## Next.js Next.js is a framework built around React. It adds routing, server rendering, static generation, and server-side features to the component model. A page can send useful HTML before the browser has built every interactive part, which is the distinction that matters in practice. ![Next.js framework logo](https://images.ctfassets.net/c63hsprlvlya/IacLLeOBR5WCvdCPqKuff/6860b5cc464c4f54703a2befa3f706b4/nextjs3.webp) That makes Next.js a natural fit for blogs, documentation, stores, and applications where the first response matters. It also gives you server-side features and API routes, although a project can still use a separate backend. The trade-off is mental overhead. You have to know what runs in the browser, what runs on the server, and when data is fetched. For a plain client-side application, that can be more machinery than the page needs. For a content-heavy application, the same machinery can save you from assembling the pieces yourself. ## Svelte Svelte moves much of the framework's work to the build step. You write a component with HTML, CSS, and JavaScript, and the compiler turns it into JavaScript that updates the page directly. Svelte does not need a virtual DOM for that update model. ![Svelte framework logo](https://codemonk.in/blog/content/images/2022/03/Svelte-Feature-Image.png) Svelte is pleasant when you want components that look close to the HTML they produce. It also works well for small widgets embedded in an existing page, where a large application framework would feel like too much. The caveat is the smaller ecosystem. Svelte's syntax can feel direct, but you may have fewer tutorials and integrations to choose from. That is a reasonable trade if the generated output and authoring style matter more than having the biggest package catalogue. ## So which one should you choose Choose React when you want the largest ecosystem and do not mind deciding how the rest of the application fits together. Choose Vue when you want a gentler template-driven start. Choose Angular when a team needs a complete structure and is willing to learn the framework's rules. Choose Next.js when React needs server-rendered or static pages. Choose Express when the job is an API or a Node server. Choose SolidJS or Svelte when their rendering models match a real requirement and the smaller ecosystems are acceptable. My winner for a general front-end learning path is React because its component model appears in so many kinds of projects. The honest downside is that a beginner can spend an afternoon picking routers and state libraries instead of building the page. For a small site, I would personally choose Vue or Svelte and keep the setup quiet. But that's just me, and your workflow might be different. If you are stuck, build the smallest version of the project first. The right choice usually becomes clearer after you know whether the hard part is the interface, the server, the data, or the team workflow. For more information, follow me on [Twitter @swapnoneel123](http://twitter.com/swapnoneel123) where I share more such content through my tweets and threads. You can also check my [GitHub(username: Swpn0neel)](https://github.com/Swpn0neel) to see my projects. ![Grammarly writing assistant banner](https://contenthub-static.grammarly.com/blog/wp-content/uploads/2019/02/bmd-4584.png) ## DevBytes: Short Coding News URL: https://www.swapnoneel.site/blog/devbytes-short-coding-news Date: 2023-03-28T15:12:34.057Z Summary: What is DevBytes? DevBytes is a comprehensive and user-friendly programming and technology news mobile application that is designed to cater to your specific interests. With its advanced algorithms, it sifts through vast amounts of information to bri... ## What is DevBytes? DevBytes is a comprehensive and user-friendly programming and technology news mobile application that is designed to cater to your specific interests. With its advanced algorithms, it sifts through vast amounts of information to bring you the latest and most helpful tech news that is tailored to your domain of interest. Unlike other tech news applications, DevBytes is highly efficient and values your time, delivering the information you need in a concise and precise manner, within a maximum of 64 words. ## Main Features DevBytes has numerous standout features that distinguish it from other offerings. Let's take a closer look at each one to understand their significance. ### Your Content, Your Choices The platform has an innovative design that includes a customizable interface, allowing users to personalize their news feed according to their preferences. Users can receive the latest breaking news, important updates, and innovative highlights through the platform's personalized news feed. Unlike other apps, this platform's approach is streamlined, ensuring that users only receive news that is relevant to them, which saves them time and effort. ### Everything within 64 words DevBytes prides itself on valuing users' time and delivering news in a fast and efficient manner. We stand out by presenting a concise 64-word news story that offers precise, easily understandable information without compromising quality or accuracy. Our focus is on streamlining the news experience by selecting relevant stories and prioritizing brevity and clarity. This means you can stay up-to-date on the latest without having to sift through unnecessary details. Our skilled team of journalists and editors work diligently to craft each news story with care and precision, ensuring that every word counts towards delivering the most essential information. Whether you're interested in the latest tech updates, new product launches, or global events, DevBytes is committed to providing you with the information you need, when you need it, in a format that respects your time and intelligence. ### "Jobs" at Hand Take advantage of amazing opportunities with DevBytes! Keep yourself informed about the newest job vacancies from various global companies that align with your area of expertise, easily accessible in a single feed. ## Additional Features - **Code Snippets:** Easily learn and apply coding tips and tricks by reading and executing the attached code on the go. - **Crypto Watch**: Stay updated on your preferred cryptocurrencies at all times using the convenient on-the-go feature. - **Deals and Discounts**: Take advantage of amazing deals and promotions on fantastic products or subscriptions. - **Product of the Day**: Discover handpicked productivity tools on your feed. ## Conclusion DevBytes is an incredible app that I've had the pleasure of using. What makes it stand out is its focus on putting its users first by providing all the amenities they may require. The app features a sleek design and a minimalistic appearance, ensuring users have the best possible experience. In today's era of rapid technological advancements, apps like DevBytes are the optimal choice. If you've ever wondered how I manage to stay up-to-date with the latest tech trends, let me share a little secret with you - it's all thanks to DevBytes. If you have enjoyed reading this blog, I recommend checking out the app available on PlayStore. You can find the link to download it here: [DevBytes](https://play.google.com/store/apps/details?id=com.candelalabs.devbytes&utm_source=app) ![Thank you blackboard illustration for DevBytes news blog](https://c0.wallpaperflare.com/preview/726/785/255/blackboard-close-up-frame-gratitude.jpg) ## Docstrings in Python with Examples URL: https://www.swapnoneel.site/blog/docstrings-in-python Date: 2023-01-25T14:42:32.056Z Summary: Python stores a docstring on __doc__, where tools and people can read it. Learn how docstrings differ from comments and how to write useful ones. ## The string Python remembers A docstring is a string literal placed immediately after the definition of a module, class, function, or method. Python stores that string on the object's **doc** attribute. That is the difference between a docstring and an ordinary explanatory sentence in a comment: programs can read the docstring later. Start with the smallest useful example: ```python def square(number): """Return the square of number.""" return number ** 2 print(square(5)) print(square.__doc__) ``` The output is: ```text 25 Return the square of number. ``` The string does not print when square runs. It becomes metadata attached to square, and the second print asks for that metadata directly. You can inspect it in a Python shell without opening the function's source file. ## Position is part of the rule Python only treats the first string expression in a definition as its docstring. Put an assignment or another statement first, and the string remains an unused literal: ```python def wrong_order(): value = 10 """This text is not the function docstring.""" return value print(wrong_order.__doc__) ``` The output is None. The string is valid Python, but it is no longer in the position Python reserves for documentation. This is a small rule, and it is easy to break when adding setup code above a docstring. The same pattern works for classes and modules: ```python class Notebook: """Store notes in memory.""" def __init__(self): self.notes = [] print(Notebook.__doc__) ``` Here the docstring describes the class as a callable object type. A module docstring follows the module's opening comments and appears before other statements in the file. ## Comments answer a narrower question Use a comment to explain a line, a workaround, or a decision inside the implementation. Python does not attach comments to the function object. Use a docstring for the public behavior that a caller needs to understand. ```python def divide(total, count): """Return total divided by count. Raises ZeroDivisionError when count is zero. """ # The explicit check gives the caller a clear rule before division. if count == 0: raise ZeroDivisionError("count cannot be zero") return total / count ``` The comment explains why the check exists. The docstring explains what divide returns and what can go wrong for someone calling it. If the function is part of a package, that distinction becomes more useful because a reader may see the docstring in an editor or generated reference page without reading the source. ## Write for the next caller A useful docstring answers the questions that the function signature does not. What does the value mean? Does the function mutate an argument? Which errors should the caller handle? Does an empty input have a special result? ```python def average(values): """Return the arithmetic mean of a non-empty sequence of numbers. Raises ValueError when values is empty. """ if not values: raise ValueError("values must not be empty") return sum(values) / len(values) print(average([2, 4, 6])) ``` The first sentence is enough for a quick read. The second tells a caller why an empty list fails. You do not need to document every obvious line, and you should not turn every short function into a wall of labels that says less than one good sentence. For a public library, a longer format can be useful when the parameters and examples are genuinely hard to infer. Keep the format consistent with the project. The format matters less than the truth of the information inside it. ## Read the documentation while debugging The built-in help function reads docstrings: ```python help(average) ``` In an interactive shell, the result may open in a pager. Press q to leave the pager when you are done. You can also use **doc** when you want the raw string, or inspect the class and method that owns the documentation. One warning: inherited or decorated functions can make the source of a docstring less obvious. A decorator that does not preserve metadata may replace the original docstring with None or with the wrapper's text. functools.wraps helps when you write decorators, but the practical fix is still to check what help() shows for the callable a user actually receives. My caveat is that a docstring is part of the interface, not a comment dump. When the implementation changes, reread it as if you were a new caller. A short description that stays true is worth more than a detailed promise the function no longer keeps. ![Thank you card in blue tones](https://img.freepik.com/free-vector/thank-you-card-blue-tones_23-2148665027.jpg?w=1380&t=st=1674657663~exp=1674658263~hmac=4b0703b3e652f76dd18a3a9e99932842361293d22d9096d3abafe7d2f2280837) ## Enumerate Function in Python with Examples URL: https://www.swapnoneel.site/blog/enumerate-function-in-python Date: 2023-02-14T08:12:35.719Z Summary: enumerate() gives you each item with its index, so loops stay readable without a manual counter. Use it with lists, tuples, strings, and custom starting points. Have you ever written `index = 0` above a loop, incremented it at the bottom, and then wondered what happens when the loop gets a `continue`? That counter works, but it gives you another piece of state to maintain. `enumerate()` puts the position beside the item for you. It accepts an iterable and returns an iterator that produces pairs such as `(0, "apple")`. Python creates each pair as the loop requests it, so you can use it without building a second list first. ## The loop you actually want The built-in function works with lists, tuples, strings, and other iterable objects: ```python # Loop over a list and print the index and value of each element fruits = ['apple', 'banana', 'mango'] for index, fruit in enumerate(fruits): print(index, fruit) ``` The loop unpacks each pair into `index` and `fruit`. The output is: ```text 0 apple 1 banana 2 mango ``` If you want to inspect every pair at once, convert the iterator to a list: ```python fruits = ['apple', 'banana', 'mango'] print(list(enumerate(fruits))) ``` That prints `[(0, 'apple'), (1, 'banana'), (2, 'mango')]`. In ordinary loops, do not add `list()` just to make the code look familiar. The loop already consumes the iterator one pair at a time, which avoids storing another collection. The same pattern is handy when you need to replace or inspect one item. For example, this prints a numbered warning only for a missing value: ```python statuses = ["ok", "missing", "ok"] for position, status in enumerate(statuses, start=1): if status == "missing": print(f"Row {position} needs attention") ``` The output is `Row 2 needs attention`. Notice that the list remains unchanged. `enumerate()` reports positions; it does not edit the iterable for you. This is usually clearer than indexing with `range(len(fruits))`: ```python for index in range(len(fruits)): print(index, fruits[index]) ``` The indexed version is not always wrong. It makes sense when you need to compare neighboring positions, assign back into a mutable list, or use the same index for several sequences. If you only need the value and its position, `enumerate()` says that directly and gives you fewer moving parts. ## Starting from one instead of zero Python uses zero-based indexes by default, so the first item gets index `0`. That is useful when the number is an index into another sequence. If you are showing positions to a person, starting at `1` usually reads better. Pass the starting value with `start`: ```python # Loop over a list and print the index (starting at 1) and value of each element fruits = ['apple', 'banana', 'mango'] for index, fruit in enumerate(fruits, start=1): print(index, fruit) ``` Now the output is: ```text 1 apple 2 banana 3 mango ``` You can format the position in the loop without changing the original list: ```python fruits = ['apple', 'banana', 'mango'] for index, fruit in enumerate(fruits): print(f'{index+1}: {fruit}') ``` The output is: ```text 1: apple 2: banana 3: mango ``` ## Lists are not special A tuple works the same way: ```python # Loop over a tuple and print the index and value of each element colors = ('red', 'green', 'blue') for index, color in enumerate(colors): print(index, color) ``` Strings are iterable too, so `enumerate()` can give you each character and its position: ```python # Loop over a string and print the index and value of each character s = 'hello' for index, c in enumerate(s): print(index, c) ``` You can pass a generator as well. That is where the lazy behavior becomes more useful: ```python def read_numbers(): for number in range(3): yield number * 10 for index, number in enumerate(read_numbers(), start=1): print(index, number) ``` The loop prints `1 0`, `2 10`, and `3 20` without asking the generator for every value in advance. You can enumerate dictionary items too. `enumerate()` supplies the position, while `.items()` supplies the key and value: ```python prices = {"tea": 20, "coffee": 30} for position, (name, price) in enumerate(prices.items(), start=1): print(position, name, price) ``` The nested unpacking may look busy the first time you see it, but the names tell you what each value means. A separate counter would add no useful information here. There are two easy mistakes. First, `start=1` changes the number reported by `enumerate()`, not the indexes stored in your list. Second, the position is not a permanent ID. If you remove items while iterating, later positions describe the current pass through the data. Changing a collection during a loop can create its own problems, so build a new collection when you need to filter or reorder values. For a normal read-only loop, `enumerate()` is the cleanest answer whenever you need an item and its position. It is small, readable, and removes the counter bug before you have to debug it. ![Handwritten thank you typography](https://img.freepik.com/free-vector/painted-thank-you-label-template_23-2148689616.jpg?w=1380&t=st=1676893691~exp=1676894291~hmac=9f0960bb4730c2bbfdc9558840a6a8ed356377041f759a66392bdfff8f0612f2) ## My First year in Tech URL: https://www.swapnoneel.site/blog/my-first-year-in-tech Date: 2023-10-24T13:00:31.327Z Summary: Exactly one year back from now, I got into University for my Bachelor's degree in Computer Science and Engineering. And it was then, I actually got introduced to the Tech world. Previously, I had some coding experience in both Java & Python and had a... Exactly one year back from now, I got into University for my Bachelor's degree in Computer Science and Engineering. And it was then, I actually got introduced to the Tech world. Previously, I had some coding experience in both Java & Python and had a proper knowledge of MySQL. In this one year, I have learned and experienced quite a few things. So, I thought it would be the perfect time to document my journey with the timelines and also share some of my plans for the next year. This may help the beginners in starting their journey and will definitely motivate me for my upcoming journey. So let's begin… ## The Journey ### October, 2022 As this was my first month of college, I focused on my academics a bit and attended classes regularly. Besides that, as I had a bit of coding knowledge, I decided to start learning Web Development. I followed the free course from [Code with Harry](https://www.youtube.com/@CodeWithHarry), which is available on YouTube. And by the end of that month, I was mostly done with HTML. It didn't take me more than two weeks to learn it as it was extremely simple and beginner-friendly. ### November, 2022 This was an interesting month for me, because it was when I got introduced to CSS, and the sheer amount of styling that I can do with it amazed me 🤩. I continued with Code with Harry and started creating some small projects including a form page for School admission, a basic site for a fictional shoe brand, etc. ### December, 2022 I decided to enhance my skills in HTML and CSS more before moving on to JavaScript, so I took a course from Coursera, which was taught by Meta. Following that, I started and completed learning Bootstrap from there. And the most interesting and a bit disappointing thing happened during this month: > The interesting thing is, that I got my first Tech freelancing gig, which consisted of building a few small projects using Bootstrap and the APIs provided by the client, for showcasing the service of their small start-up. And, it was a 15K rupees gig. Now, the disappointing part is, that I couldn't complete the gig because some part of it was extremely out of my league. But they paid me 8K rupees for the work I had done. **The most important thing I learned was that real-life development is all about googling, pasting codes from StackOverflow, and learning while doing so…** Took full advantage of my Winter vacation, to start learning some advanced concepts of Python and finally got started with JavaScript!! ### January, 2023 This is that single month where I did a lot of "**first-time**" things and learned & got started with them. Continued learning JavaScript from both Code with Harry and Coursera. And, I started building some small projects using my newly learned concepts from Python. Now with the skills I have learned so far, I was thinking of putting them to the test by attending a Hackathon. So, I grabbed one of my classmates and a 2nd-year guy from another college. Our small team had a diverse skill set, and we decided to participate in "**Treasure Hacks 3.0**". Our project was "Lab for All" (Website link: [https://labforall.vercel.app](https://labforall.vercel.app), GitHub link: [https://github.com/Swpn0neel/treasure-hacks-labforall](https://github.com/Swpn0neel/treasure-hacks-labforall)) > We planned to make a project on the Ed-Tech field and the idea was to create a platform where the users can get assistance, particularly in their lab assignments, from video lectures to notes, and viva questions to 3D simulations. > > While working on the project, I got to know about how you have to make the site design before starting to code, and that attracted me a lot and planted the seed inside me to start designing in Figma, which I planned to learn in the near future. > > Strangely enough, till now I haven't learned about Git and GitHub. But I had to grasp it now for the obvious reason of collaboration in my Hackathon project. And it was a significant bump in my Tech learning journey. > > Also, while doing the project I had to learn Firebase as it was needed for data storing and user authentication, and it was my part to complete that job. The best part is, we completed the project within the time limit and implemented all the planned features. And to our amazement, **we won the Hackathon being the second runner-up**. Also, this month, I wrote my first blog at Hashnode on Python and also got active on [Twitter (now X)](https://twitter.com/swapnoneel123), and started posting content on both mediums. ### February, 2023 Our first-semester final exam happened this month and was stretched over a period of 20 days. And later, when the results came out, I topped my batch with an SGPA of 9.33. This month, I also focused on my blogging and wrote around 8-9 articles. Also, I started working on my [personal portfolio website](http://swapnoneel.vercel.app) and completed it. It took just over two weeks to do that. Learned a bit of React and JavaScript while building my portfolio, and also worked on file handling and its modules in Python. [Check out the tweet/post](https://twitter.com/swapnoneel123/status/1630930494840864768) ### March, 2023 After a lot of grinding web development for some months, I decided to switch the gear a bit and decided to start doing DSA. Now for doing that, I rarely used any guide or video tutorials, but relied on solving more and more Leetcode problems (you can check my [Leetcode profile, here](https://leetcode.com/Swapnoneel/)) based on my previous knowledge of arrays, strings and recursion. By doing so, I solved just over 30 problems and approached around 100 problems in that venture. Also, I started learning UI/UX design and gained a huge interest in that. Followed Jesse Showalter, Design Course and Payton Clerk on YouTube to get knowledge about designing. I used Figma for the design purpose!! Besides these, I completed the "7 days, 7 blogs" challenge on Hashnode and mostly shared my Python knowledge there. And also, explored a lot of Python libraries mainly about text and image analysis. ### April, 2023 Continued my DSA journey and solved over 20+ questions based on String Builder, Array List and Arrays. Also, I decided to go for another Hackathon along with two of my classmates. It was one of the **MLH hacks**, called "**Hack Around the World 2.0**". Our project was "**The Magnificent Seven**" (Website link: [https://magnificent-seven.vercel.app](https://magnificent-seven.vercel.app), GitHub link: [https://github.com/Swpn0neel/TheMagnificentSeven](https://github.com/Swpn0neel/TheMagnificentSeven))We decided to use Three.js, Svelte and Tailwind CSS for the project. That's why, I started learning Svelte and Tailwind CSS. Also, I created the Figma design of the site. This was a much smaller scale project than the previous one but cramped most of our technical skills into it. And again, to our amazement, we managed to be the **Winner of this Hackathon**. ### May, 2023 Paused DSA for a bit, but started learning C as it was a part of my college curriculum. Also, I delved more into designing and started mastering animations and transitions in CSS and decided to build some small-scale projects using that. In that venture, I created around 5-6 projects that focused mainly on uniqueness and smooth animations. Check them out here: [https://github.com/Swpn0neel/Web-Development-Projects](https://github.com/Swpn0neel/Web-Development-Projects) And the most important thing that happened at the end of the month. Previously, I mentioned that I wrote a number of blogs regarding Python in the months of February and March. Now, they got noticed by **Tutorials Point**, and I got a part-time job offer from them, whose first contract was to deliver an advanced Python course for their platform. I accepted the offer and started working for them. ### June, 2023 This was a really special month, because I only earned, earned and earned in this one. Aside from Tutorials Point, I got some really good freelancing offers too: I created the UI/UX Design and wrote SEO-optimized content for some medium-scale businesses. Delivered some copywriting and technical writing jobs. Also, this was the month of our second-semester exam. I was extremely loose academically this semester and didn't have high hopes about scoring. But still, some fruitful one-nighters helped me to get an SGPA of 9.14. ### July, 2023 Took the after-semester break to my advantage and again went for an earning streak with various freelancing job offers. For some time I hadn't attended any hackathons, so in the middle of the month, our same team from MLH Hacks decided to attend our first offline hackathon and we chose Eastern India's largest Hackathon, Hack 4 Bengal 2.0 for that. Everything was going nicely there, but one of our teammates faced health issues, and that's why we had to return to our home before the Hackathon ended. It was a disappointing failure for us. But for me personally, it was extremely fruitful from an earning standpoint. ### August, 2023 The month started with me hosting an introductory session to Open-Source at my University for my fellow peers, along with two other folks from my batch. It was my first mentoring and public speaking session. Also, an important thing that caught my eye was that, I haven't learned a lot of things personally (except Python, most of my Freelancing works were dependent on that and I learned a lot of Python libraries during that time), as I was majorly focusing on earning. Also, I haven't touched DSA for some months. So, I again started Leetcoding at the end of this month. Previously, I had completed 50+ problems, I added 10+ to that number this month. ### September, 2023 Rejected all Freelancing offers this month and went hard on Leetcoding. I solved more than 90 problems this month, ranging from Linked List and Prefix Sum to Siding Window and Recursion problems. Also, I started preparing for the Smart India Hackathon '23. Involved two juniors in our team so that they can have their first Hackathon experience and learn along the way. As part of the national hackathon, we have to qualify for the internal Hackathon of our University, MAKATHON '23. We qualified with flying colors taking the first position in the Hackathon. ### October, 2023 Decided to make some complex projects related to Web Development and prepared some web designs on Figma for the same, while learning a bit more about UI/UX Designing in general. Started taking weekly mentorship sessions for my juniors, so that they could learn at a better pace and not get distracted while being focused on the ultimate goal of upskilling themselves. ## Bonus I have mentioned almost everything here. But I want to mention some things that weren't mentioned in the timeline. - Python and its vast library have always been with me throughout my learning journey and earned me the majority of my bucks through freelancing. - I was able to purchase an ASUS TUF A15 laptop all by myself through freelancing ventures, and also manage a lot of my personal expenses besides making a little bit of savings for myself. - Since January, I've been active on Twitter and made a family of over 500 folks. - College academics were never my priority and I tried to ignore it as much as possible and gave the least possible amount of time to it. - Although I started my journey with the help of video tutorials, as time passed I majorly relied on official documentation and blogs because of their simplicity and least time requirement. - Other than the mentioned stuff, I had to learn quite a few other things like API management, handling ML models, and many more for project requirements. It's impossible for me to mention all of them in a single blog 😅. ## My plans for the Second year So far, I have completed 170+ Leetcode problems, and my target moving forward is to cover all the DSA concepts in the 2nd year and at least reach the 600+ problems mark on Leetcode. Also, I'll start attending coding contests to put my skills to the test. I will start delving into AI/ML and would like to get a solid grip on it by October 2024. Along with that, I plan to make some SaaS products with my knowledge of AI/ML and Web Development. Also, I'll attend more and more hackathons, because the learning opportunity from there is just undeniable. And, I would start focusing on subjects like System Design and Operating Systems. Plus, I would be actively looking forward to grabbing any internship opportunities. And lastly, I would continue mentoring my fellow juniors and would start focusing on making Open-Source contributions to renowned organizations. ## Conclusion And that's a wrap!! But if you have any queries regarding my journey or any suggestions for my future, I would love to hear that from you in the comments. Also, as a closing note, I would like to share my resume which I have created recently based on my progress so far, you can check out [my resume document](https://docs.google.com/document/d/1c931FkeAugaefH0LcY2Irap0UOp4D9cLpyAgwjOt1ao/edit?usp=sharing). And if you want to stay connected with me, you can follow me here or on my other social handles. Thank You for reading about my journey, have a nice day ahead!! ![First year in tech reflection banner](https://cdn.hashnode.com/res/hashnode/image/upload/v1698152151751/048427f8-d108-47e0-9f9d-79b1596fa335.png) ## Object Introspection in Python Explained URL: https://www.swapnoneel.site/blog/object-introspection-in-python Date: 2023-02-07T15:48:04.393Z Summary: Python's dir(), __dict__, id(), and help() let you inspect objects while code runs. Use them to understand attributes, identity, and documentation during debugging. ## What introspection means Introspection means asking an object about itself while the program is running. It is useful when you are learning an unfamiliar class, checking why an attribute lookup behaves strangely, or debugging a value that is not what you expected. Python gives you several built-ins for this. They answer different questions, so do not treat their output as a complete description of an object. The questions are worth separating. `type()` tells you what kind of value you have. `dir()` tells you which names might be available. `getattr()` reads a name when you only know it at runtime. `help()` points you toward the documented interface. These tools are most useful when you use the smallest one that answers the question in front of you. ## The dir() function lists names `dir(value)` returns a sorted list of names that Python considers useful for that object. The list can include methods, attributes, and dunder names inherited from a class. It is a discovery tool, not a guarantee that every name can be called successfully. ```python my_list = [1, 2, 3] names = [name for name in dir(my_list) if name in {"append", "pop"}] print(names) ``` The output is: ```text ['append', 'pop'] ``` Use `dir()` when you know roughly what you are searching for. Reading the entire list for a large object can be noisy, so filtering it often makes the result easier to use. The presence of a name does not prove that it is safe to call. A property may run code when read, and a method may need arguments. Treat `dir()` as a menu of possibilities, then inspect the signature or documentation before using an unfamiliar entry. ## The **dict** attribute shows stored attributes Many Python objects keep their instance attributes in a dictionary called `__dict__`. It is an attribute, not a function, so write `person.__dict__`, not `person.__dict__()`. ```python class Person: def __init__(self, name, age): self.name = name self.age = age person = Person("John", 30) print(person.__dict__) ``` The output is: ```text {'name': 'John', 'age': 30} ``` `__dict__` shows attributes stored directly on this instance. A class can also have a `__dict__`, and some objects, including classes that use `__slots__`, do not expose an instance dictionary. If the attribute is missing, that does not mean the object has no state. When the attribute name comes from a configuration value, use `getattr()` instead of constructing an expression: ```python class Settings: timeout = 30 settings = Settings() name = "timeout" print(getattr(settings, name, 10)) ``` The third argument is a default for a missing attribute. `hasattr()` can answer whether a lookup succeeds, but remember that it may execute a property and can hide an exception raised during that lookup. For code you control, a direct access with a clear `AttributeError` is often easier to debug. ## The id() function identifies an object during its lifetime `id(value)` returns an integer that stays the same for that object while it exists. Two live objects cannot have the same identity value. The exact number is implementation-dependent, so it may change between runs. Use the `is` operator when you want to test identity: ```python first = [] second = first third = [] print(first is second) print(first is third) print(id(first) == id(second)) ``` The output is: ```text True False True ``` Do not use `id()` to compare equal values. Two separate lists can contain the same items while representing different objects. Use `==` for value comparison and `is` for identity comparison. ## The help() function reads documentation `help(value)` opens Python's built-in documentation for a value. It can show a module, class, function, method, or object. In an interactive shell, Python may open the result in a pager, so press `q` when you are done reading. ```python help(str.upper) ``` The result includes the method's description and signature when that information is available. `help()` is especially handy when you remember a method name but not its arguments. `callable(value)` is another small check that helps during exploration. It tells you whether Python can call the value, while `isinstance(value, SomeType)` lets you check whether it fits a type or protocol you explicitly care about. Avoid turning a debugging tool into a maze of type checks; in ordinary Python code, trying the operation and handling a meaningful error can be clearer. ## A practical debugging routine When an unfamiliar object appears in your code, start with `type(value)` to identify its class. Filter `dir(value)` to find possible names, inspect `value.__dict__` when the object provides one, and use `help()` for the documentation. Use `getattr()` when the name is dynamic and `id()` only when the question is whether two names point to the same object. My caveat is that introspection shows what Python exposes, not what the author intended. Treat the output as a clue, then read the class or docstring before changing code based on a guess. Printing an object's attributes can help you find a problem, but it is not a replacement for a documented interface or a test that explains the expected behavior. ![Thank you placard concept illustration](https://img.freepik.com/free-vector/thank-you-placard-concept-illustration_114360-13436.jpg?w=1380&t=st=1675784022~exp=1675784622~hmac=b4748b9ac8dd94ff98a8232e0a56aa06102f42d9595f55a3b7cdc17121e72ea8) ## OS Modules in Python with Examples URL: https://www.swapnoneel.site/blog/os-modules-in-python Date: 2023-02-12T13:58:39.078Z Summary: The Python os module connects your code to the operating system. Read and write files, inspect folders, create directories, and run shell commands with care. Python usually lets you forget that a program is running on top of an operating system. You call `open()`, read a string, and carry on. The `os` module is where that boundary becomes visible: folders have names, files have descriptors, processes have exit codes, and each operating system has its own path rules. You do not need to memorize the whole module. Think in three buckets. Are you working with a path, with a file at a lower level, or with a command that another process should run? The answer points you toward a different part of `os`. ## Opening files at the lower level Most Python code should use the built-in `open()` function because it gives you a convenient file object and closes it neatly with `with`. `os.open()` is lower level. It returns an integer file descriptor, and you are responsible for reading bytes and closing that descriptor. This complete example creates a small file, reads it back, and removes it at the end. Run it in a scratch directory if you want to watch the file appear: ```python import os path = "os-module-demo.txt" try: fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o644) try: os.write(fd, b"Hello from a file descriptor!") finally: os.close(fd) fd = os.open(path, os.O_RDONLY) try: contents = os.read(fd, 1024) print(contents.decode("utf-8")) finally: os.close(fd) finally: if os.path.exists(path): os.remove(path) ``` The printed text is `Hello from a file descriptor!`. The flags explain the first open call: `O_WRONLY` requests writing, `O_CREAT` creates the file if it is missing, and `O_TRUNC` clears old contents. The numeric mode controls permissions on systems that use them. The `try` and `finally` blocks matter because leaving a descriptor open can exhaust the process's file handles. For normal text files, this is more work than you need. Use `os.open()` when an API requires a descriptor or when you genuinely need low-level flags. The built-in file object is the better default. ```python import os # The high-level version closes the file for you with open("message.txt", "w", encoding="utf-8") as file: file.write("Hello, world!") ``` ## Inspecting paths and folders `os.listdir()` returns the names inside a directory as strings. The result is not sorted, so sort it when the order is part of what a person will read: ```python import os files = sorted(os.listdir(".")) print(files) ``` `os.mkdir()` creates one directory and raises an error if the directory already exists. `os.makedirs()` is more useful when the path may contain missing parents: ```python import os os.makedirs("reports/2023", exist_ok=True) ``` The `exist_ok=True` argument makes a second run harmless. For paths that combine several parts, use `os.path.join()` instead of typing `/` or `\\` yourself: ```python import os config_path = os.path.join("config", "settings.json") print(config_path) ``` The printed separator depends on the operating system. That is why joining path parts is safer than assembling one string by hand. You can also inspect a value without guessing what it means: ```python import os print(os.path.abspath("reports")) print(os.path.exists("reports")) ``` The first line gives the full path, while the second prints `True` if the directory exists. These checks still have a race condition if another process changes the filesystem immediately afterward, so handle the actual operation's exception as well when the code matters. ## Asking the shell to do something `os.system()` sends a string to the system shell and returns an exit status. It does not give your Python code the command's printed text: ```python import os status = os.system("echo Hello from the shell") print(f"exit status: {status}") ``` The shell prints `Hello from the shell`, then Python prints a status. A zero status generally means the command completed successfully, but the exact value can be represented differently across platforms. `os.popen()` gives you a file-like object for command output: ```python import os with os.popen("echo Hello from the shell") as output_file: output = output_file.read() print(output.strip()) ``` Do not build a shell command by joining untrusted user input into a string. Shell metacharacters can change what actually runs. For new code that needs arguments, error handling, or separate output streams, use `subprocess.run()` with a list of arguments instead. The `os` shortcuts are useful for learning the boundary, but they are easy to outgrow. ## The practical rule Start with Python's high-level file and path tools. Reach for `os.path`, `os.listdir()`, or `os.makedirs()` when you need to inspect the machine. Use `os.open()` and shell calls only when their lower-level behavior is the reason you are writing the code. That boundary keeps the module useful without turning every file operation into a permissions and cleanup puzzle. ![Thank you placard concept illustration](https://img.freepik.com/free-vector/thank-you-placard-concept-illustration_114360-13436.jpg?w=1380&t=st=1675784022~exp=1675784622~hmac=b4748b9ac8dd94ff98a8232e0a56aa06102f42d9595f55a3b7cdc17121e72ea8) ## Sets in Python with Examples URL: https://www.swapnoneel.site/blog/sets-in-python Date: 2023-01-25T18:09:15.443Z Summary: Python sets store unique hashable values and make membership, union, intersection, difference, and symmetric difference easy. You have a list of email addresses and want to know who has already signed up. You can scan the list every time, or you can put the addresses in a set and ask one direct question: `email in subscribers`. A set is a mutable collection of unique, hashable values. It is built for membership checks and operations between groups, not for keeping items at numbered positions. That one distinction explains most of the behavior that surprises people at first. Curly braces create a set when they contain values. Repeated values collapse immediately: ```python info = {"Carla", 19, False, 5.9, 19} print(info) ``` The printed order can vary, and the duplicate `19` appears only once: ```text {False, 19, 5.9, 'Carla'} ``` Do not build logic around that printed order. A set has no list-like index, so `info[0]` raises a `TypeError`. For an empty set, use `set()`, because `{}` creates an empty dictionary: ```python empty_set = set() empty_dictionary = {} print(type(empty_set).__name__) print(type(empty_dictionary).__name__) ``` The values inside a set must be hashable. Strings, numbers, tuples containing hashable values, and booleans work. A list does not: ```python values = {"ready", ["not", "hashable"]} ``` That code raises `TypeError: unhashable type: 'list'`. A mutable list could change after insertion, which would make its lookup position unreliable. If you need a fixed collection inside a set, use a tuple when its contents are hashable. ## Iterating and checking membership Iterate over a set with a `for` loop. Each value appears once, but the order may differ between runs: ```python info = {"Carla", 19, False, 5.9} for item in info: print(item) ``` The loop visits each value once. If you need predictable output for a report or a test, sort a compatible set first: ```text False 5.9 19 Carla ``` For a membership check, the syntax is much shorter: ```python allowed_roles = {"admin", "editor", "viewer"} role = "editor" if role in allowed_roles: print("access granted") ``` The set does not tell you where `"editor"` is. It answers whether the value belongs to the group. That is the contract you should design around. ## Combining groups Set operations use the same ideas you may have seen in mathematics. A union collects values from either set, an intersection keeps values found in both, and a difference keeps values found on one side only. The methods return new sets unless their name ends in `_update`. ### Union without changing either set `union()` returns a new set and leaves both inputs alone: ```python cities = {"Tokyo", "Madrid", "Berlin", "Delhi"} cities2 = {"Tokyo", "Seoul", "Kabul", "Madrid"} cities3 = cities.union(cities2) print(sorted(cities3)) print(sorted(cities)) ``` The first line is `['Berlin', 'Delhi', 'Kabul', 'Madrid', 'Seoul', 'Tokyo']`, while the second still contains only the original four cities. The `|` operator is a shorter spelling for the same non-mutating operation: ```python all_cities = cities | cities2 ``` When you do want to change `cities`, use `update()`: ```python cities = {"Tokyo", "Madrid", "Berlin", "Delhi"} cities2 = {"Tokyo", "Seoul", "Kabul", "Madrid"} cities.update(cities2) print(sorted(cities)) ``` Here `cities` itself has changed. This difference between a new result and an in-place update is worth checking before you pass a set into another function. ### Shared values with intersection `intersection()` keeps values found in both sets and returns a new set: ```python cities = {"Tokyo", "Madrid", "Berlin", "Delhi"} cities2 = {"Tokyo", "Seoul", "Kabul", "Madrid"} cities3 = cities.intersection(cities2) print(sorted(cities3)) ``` The output is `['Madrid', 'Tokyo']`. The `&` operator expresses the same idea as `cities & cities2`. The update form keeps only the shared values in the original set: ```python cities = {"Tokyo", "Madrid", "Berlin", "Delhi"} cities2 = {"Tokyo", "Seoul", "Kabul", "Madrid"} cities.intersection_update(cities2) print(sorted(cities)) ``` Now `cities` contains only the shared values. Use this form when you own the set and intentionally want to discard the other values. ### Values that belong to one side `symmetric_difference()` keeps values that belong to one set but not both: ```python cities = {"Tokyo", "Madrid", "Berlin", "Delhi"} cities2 = {"Tokyo", "Seoul", "Kabul", "Madrid"} cities3 = cities.symmetric_difference(cities2) print(sorted(cities3)) ``` The output is `['Berlin', 'Delhi', 'Kabul', 'Seoul']`. The `^` operator is the shorter form. If you call the update version, `cities` changes in place: ```python cities = {"Tokyo", "Madrid", "Berlin", "Delhi"} cities2 = {"Tokyo", "Seoul", "Kabul", "Madrid"} cities.symmetric_difference_update(cities2) print(sorted(cities)) ``` The result is the same four one-sided values, stored back in `cities`. ### Values missing from the other set `difference()` is directional. It keeps values in the first set that are missing from the second: ```python cities = {"Tokyo", "Madrid", "Berlin", "Delhi"} cities2 = {"Seoul", "Kabul", "Delhi"} cities3 = cities.difference(cities2) print(sorted(cities3)) ``` The output is `['Berlin', 'Madrid', 'Tokyo']`. Reversing the operands gives a different answer because `cities2 - cities` means something else. `difference_update()` stores the result back in the first set: ```python cities = {"Tokyo", "Madrid", "Berlin", "Delhi"} cities2 = {"Seoul", "Kabul", "Delhi"} cities.difference_update(cities2) print(sorted(cities)) ``` Now `cities` contains only cities that were not in `cities2`. You can also ask whether one set contains another with `issubset()` and `issuperset()`, or test whether two sets share nothing with `isdisjoint()`. These methods read like the question you are asking, which is usually better than writing a manual loop. My practical judgment is that sets are the right tool for membership and group comparison, and a bad tool for ordered output. If the order is part of the result, keep a list or convert the set to a sorted list at the boundary where you display it. The set should answer "does this belong?"; a list should answer "what comes next?". ![Thank you banner graphic for Python sets blog](https://images.pexels.com/photos/2072165/pexels-photo-2072165.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1) ## String Formatting in Python with Examples URL: https://www.swapnoneel.site/blog/string-formatting-in-python Date: 2023-01-25T07:50:52.768Z Summary: Python string formatting turns values into readable text. Compare str.format() and f-strings, then use format specifications for prices, percentages, and separators. Printing a value is easy. Printing it in the exact shape a person expects takes a little more thought. A price may need two decimal places, a percentage may need a percent sign, and a report line may need every column to line up. String formatting separates the message from the rule for displaying each value. Python gives you a few styles, but `str.format()` and f-strings cover most code you will write today. ## Using str.format() The `str.format()` method replaces placeholders inside a string. A format specification after the colon controls the output: ```python txt = "For only {price:.2f} dollars!" print(txt.format(price = 49)) ``` Here `price` is inserted with two digits after the decimal point, so the output is `For only 49.00 dollars!`. The original number remains an integer; only its text representation changes. The braces can contain a position or a name. Positional placeholders start at zero: ```python message = "{} scored {} points." print(message.format("Maya", 42)) ``` Named placeholders make a template easier to read when it has several values: ```python message = "{name} has {count} messages." print(message.format(name="Maya", count=42)) ``` The values do not need to be strings. Python converts them while building the final text, and the format specification tells it how to display a value. Named fields are especially useful when the same template is stored in one place and values arrive later. You can also align text and numbers: ```python rows = [("Tea", 3), ("Coffee", 12)] for name, count in rows: print("{:<10} {:>3}".format(name, count)) ``` The output is: ```text Tea 3 Coffee 12 ``` `<` aligns to the left and `>` aligns to the right. The `10` and `3` are field widths, not limits on the values. A long string can still spill past its field. ## Writing f-strings An f-string puts the letter `f` before the opening quote. Expressions inside `{}` are evaluated when Python creates the string: ```python name = "Tushar" age = 23 print(f"Hello, my name is {name} and I am {age} years old.") ``` The output is: ```text Hello, my name is Tushar and I am 23 years old. ``` You can put an expression inside the braces, not just a variable: ```python items = 4 price = 12.5 print(f"Total: {items * price:.2f}") ``` This prints: ```text Total: 50.00 ``` The part after the colon is a format specification. `.2f` asks for a floating-point value with two digits after the decimal point. Python evaluates `items * price` first, then applies `.2f` to the result. The same syntax handles percentages, alignment, and thousands separators: ```python completion = 0.875 total = 1250000 print(f"Completion: {completion:.1%}") print(f"Total: {total:,}") ``` This prints `Completion: 87.5%` and `Total: 1,250,000`. The stored values are still `0.875` and `1250000`; formatting changes what the reader sees. If you need a literal brace, double it: ```python name = "Maya" print(f"{{user}}: {name}") ``` The output is `{user}: Maya`. A single unmatched brace causes a `SyntaxError`, which is an annoying failure when a message is assembled from several pieces, so keep complicated templates readable. ## Which style should you use F-strings are usually easier to read when the values are already in scope. `str.format()` is still useful when the template is stored separately or when you want to pass named values explicitly. Older code may use percent formatting, such as `"Hello, %s" % name`. It still works, but several values and format rules become harder to read. When you control the Python version, reach for an f-string first, then use `str.format()` when the template and values need to stay separate. There is a safety boundary here. An f-string evaluates its Python expressions immediately, and formatting does not validate a value or escape it for HTML, SQL, or a shell command. Use the output library's escaping or parameter handling for those contexts. A nicely formatted string can still be unsafe input. My default is f-strings for local variables and calculations, `str.format()` for reusable templates, and neither one as a substitute for validation. Once you know whether you are changing the value or only changing its display, the syntax becomes much less mysterious. ![Thank you graphic for Python string formatting blog](https://images.pexels.com/photos/2072165/pexels-photo-2072165.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1) ## map(), filter(), and reduce() in Python Explained URL: https://www.swapnoneel.site/blog/the-3-most-powerful-functions-in-python Date: 2023-02-10T12:46:31.060Z Summary: map(), filter(), and reduce() each pass a function over data in a different way. Learn what they return, when they help, and when a loop is clearer. The title calls these functions powerful, but the useful part is much less dramatic. `map()`, `filter()`, and `reduce()` each describe one shape of work over a collection. If you can name the shape, you can decide whether one of them makes the code clearer or whether a normal loop is the better answer. All three accept a function as an argument. A function that receives another function is called a higher-order function, which sounds academic until you see the three questions they answer: - Should every item become a new value? - Should some items be kept and the rest discarded? - Should many values become one result? ## Transforming every item with map `map(function, iterable)` applies the function to each item. It returns a lazy iterator, so wrap it in `list()` when you need all results at once: ```text map(function, iterable) ``` ```python numbers = [1, 2, 3, 4, 5] doubled = map(lambda x: x * 2, numbers) print(list(doubled)) ``` The lambda receives one number at a time. `map()` passes each number through the multiplication, and `list()` consumes the iterator to produce `[2, 4, 6, 8, 10]`. If the transformation already exists as a function, pass that function directly: ```python names = ["asha", "mina", "rohan"] upper_names = map(str.upper, names) for name in upper_names: print(name) ``` There is no need to write `lambda name: name.upper()` here. The direct function keeps the operation visible, and the loop consumes the lazy iterator one item at a time. The iterator is single-use: ```python numbers = [1, 2, 3] doubled = map(lambda number: number * 2, numbers) print(list(doubled)) print(list(doubled)) ``` The second print is `[]` because the iterator has already been consumed. That surprises people when they store a `map()` result and expect it to behave like a list. For a simple transformation, I usually prefer a list comprehension because the result type and rule are obvious at a glance: ```python doubled = [number * 2 for number in numbers] ``` ## Keeping matching items with filter `filter(predicate, iterable)` keeps an item when the predicate returns a truthy value. A predicate is simply a function used to answer a yes-or-no question: ```text filter(predicate, iterable) ``` ```python numbers = [1, 2, 3, 4, 5] evens = filter(lambda x: x % 2 == 0, numbers) print(list(evens)) ``` The predicate returns `True` for even values, so the result is `[2, 4]`. A comprehension expresses the same rule directly and is often easier to debug: ```python evens = [number for number in numbers if number % 2 == 0] ``` Give a predicate a name when the condition carries business meaning: ```python def is_available(product): return product["stock"] > 0 and not product["archived"] products = [ {"name": "Notebook", "stock": 3, "archived": False}, {"name": "Pen", "stock": 0, "archived": False}, ] available = list(filter(is_available, products)) ``` The named function gives you a place to test the rule independently. A lambda is fine for `number % 2 == 0`; it becomes a distraction when the condition needs another explanation. ## Combining values with reduce `reduce()` repeatedly combines two values until one result remains. Unlike `map()` and `filter()`, it is not a built-in name, so import it from `functools`: ```text reduce(function, iterable) ``` ```python from functools import reduce numbers = [1, 2, 3, 4, 5] total = reduce(lambda left, right: left + right, numbers) print(total) ``` The first call combines `1` and `2`, producing `3`. The next call combines that result with `3`, then continues until the total is `15`. This is a left-to-right chain, not a mysterious shortcut. For addition, `sum(numbers)` is clearer. `reduce()` also needs a decision for an empty iterable. Without an initial value, `reduce()` raises `TypeError` when there is nothing to combine: ```python from functools import reduce print(reduce(lambda left, right: left + right, [], 0)) ``` The final `0` is the initial value, so this version prints `0`. You can also combine values into something other than a number, but check whether a normal operation says the same thing more clearly. For example, a sentence is better built with `" ".join(words)` than with a reduction that keeps adding strings. `reduce()` earns its place when the repeated combination is the useful idea, not merely because it can express the answer. The three functions can form a pipeline when each step has a separate job: ```python prices = [5, 12, 20, 3] eligible = filter(lambda price: price >= 5, prices) with_tax = map(lambda price: price * 1.18, eligible) total = sum(with_tax) print(total) ``` This works because `filter()` and `map()` stay lazy until `sum()` consumes them. If the callbacks start needing several lines or side effects, stop and write a loop. A little repetition is easier to inspect than a pipeline that hides the state changes. My caveat is that nested reductions can make a small calculation harder to debug than a normal loop. `map()` and `filter()` are fine when their iterator behavior is clear. For many everyday transformations, comprehensions read better. Use `reduce()` when the repeated combination is genuinely the point, then name the operation clearly. The judgment is straightforward: use `map()` to change every value, `filter()` to select values, and `reduce()` to collapse values into one result. But short syntax is not automatically readable syntax. If a loop explains the rule faster, write the loop. ![Thank you banner image](https://www.incimages.com/uploaded_files/image/1920x1080/getty_469566889_105923.jpg) ## Virtual Environments in Python Explained URL: https://www.swapnoneel.site/blog/virtual-environments Date: 2023-01-27T16:11:48.129Z Summary: Python virtual environments keep each project's interpreter and packages separate. Create, activate, deactivate, and record one with requirements.txt. Your Python installation is shared by default. That feels convenient until one project needs an older package and another project needs a newer one. Install both globally and you have made the interpreter responsible for an argument it cannot resolve cleanly. A virtual environment is a project-specific directory containing an interpreter and its installed packages. It lets each project choose its own dependencies without changing every other project on the machine. The environment is not a container and it does not install a second operating system. It is a boundary around Python packages. That boundary does not make the source code portable by itself. You still need to record the packages and the Python version your project expects. The environment is disposable; the dependency description is the part you keep. ## Creating the environment Python includes the `venv` module. Run this command from the project directory. It creates a directory named `myenv`: ```bash python -m venv myenv ``` Activate it with the command for your shell: ```bash # Linux or macOS source myenv/bin/activate # Windows PowerShell .\myenv\Scripts\Activate.ps1 # Windows Command Prompt myenv\Scripts\activate.bat ``` After activation, your shell usually shows `(myenv)` in the prompt. More useful than the prompt is checking which interpreter is running: ```bash python -c "import sys; print(sys.executable)" ``` The printed path should point inside `myenv`. Commands such as `python` and `pip` now use the environment, so an install stays with this project. I prefer `python -m pip` because it makes the connection between the Python interpreter and its package installer explicit: ```bash python -m pip install requests ``` If PowerShell blocks the activation script with an execution-policy error, that is a shell policy problem, not a broken Python environment. You can still run the environment's interpreter directly, or adjust the policy according to your machine's rules. Do not copy a policy command from a random post without understanding whether it changes the policy for only your user or for the whole machine. ## Leaving the environment When you finish working, leave the environment with: ```bash # Deactivate the virtual environment deactivate ``` The command only changes the current shell. It does not delete the environment or uninstall its packages. If you close the terminal, the environment directory remains on disk. ## Recording the dependencies Do not commit the `myenv` directory to your repository. It contains machine-specific paths and can become large. Record the packages your project needs in `requirements.txt` instead: ```bash python -m pip freeze > requirements.txt ``` On another machine, create and activate a fresh environment, then install those recorded versions: ```bash python -m pip install -r requirements.txt ``` One honest caveat: `pip freeze` records everything installed in the environment, including packages you may have added while experimenting. For a small project that is often fine. For a long-lived project, review the file before committing it. A clean environment makes that review much easier. Add the environment directory to `.gitignore` as well: ```text myenv/ .venv/ venv/ ``` The exact directory name is your choice. `.venv` is common too; consistency matters more than the name. For a small tutorial, `requirements.txt` is enough. A package or a larger application may eventually use `pyproject.toml` to describe dependencies and build settings. The file format can change, but the workflow does not: a new machine should be able to create a clean environment from a short, reviewable declaration. If the environment gets confused, recreate it instead of trying to repair every installed package by hand. First update the dependency file, remove the disposable environment directory, and run `python -m venv` again. The source code is outside that directory, so rebuilding it is normally the safer fix. ## The routine that keeps it useful Create the environment once, activate it whenever you work on the project, install packages through `python -m pip`, and deactivate it when you are done. When a teammate checks out the project, they can recreate the environment from `requirements.txt` instead of receiving a copy of yours. The routine is not glamorous, and it does not solve every deployment problem. It does solve the common mistake of installing a package into one interpreter and running the program with another. For Python projects, that is enough reason to make virtual environments your default. ![Thank you banner image](https://www.incimages.com/uploaded_files/image/1920x1080/getty_469566889_105923.jpg)