Blog

  • I Thought I Understood async/await… Until I Asked One Simple Question

    I Thought I Understood async/await… Until I Asked One Simple Question

    A few days ago, I came across a post explaining async and await in C#. It talked about state machines, ThreadPool, asynchronous operations, and methods resuming after await.

    I read the entire post.

    Honestly…

    I understood almost nothing.

    Like many developers, I had a few assumptions in my head:

    • Does async create a new thread?
    • Does await stop the thread?
    • If the method pauses, what exactly is paused?
    • When execution resumes, why doesn’t it immediately execute the next line?
    • If another thread continues the work, how does the program know where to continue?

    The more I thought about it, the more confused I became.

    So I started asking questions.

    The first realization completely changed my understanding:

    async does not create a new thread.

    That surprised me.

    For years, I subconsciously associated async with “background thread.”

    It isn’t.

    async simply tells the compiler that the method contains asynchronous operations and can be paused and resumed.

    Then came the second realization:

    await pauses the method, not the thread.

    That sentence looks simple, but it took me a while to truly understand it.

    Imagine an ASP.NET Core API.

    A request arrives, and a ThreadPool thread starts executing your controller action.

    Eventually, the code reaches this line:

    await _repository.GetCustomerAsync();
    

    The database now needs a few seconds to respond.

    My original assumption was that the thread would just sit there waiting.

    Wrong.

    The thread is released back to the ThreadPool so it can serve another request.

    Your method remembers exactly where it stopped.

    When the database finishes, .NET picks an available thread and continues executing the method from the line after await.

    Another question immediately came to my mind.

    “If execution continues after await, shouldn’t the next line execute immediately?”

    Then I realized something I had completely overlooked.

    Consider this example:

    public async Task SaveCustomerAsync()
    {
        Console.WriteLine("Step 1");
    
        await SaveToDatabaseAsync();
    
        Console.WriteLine("Step 2");
    }
    
    public async Task SaveToDatabaseAsync()
    {
        await Task.Delay(5000);
    
        Console.WriteLine("Database Saved");
    }
    

    Initially, I expected the output to be:

    Step 1
    Step 2
    Database Saved
    

    But that’s not what happens.

    The actual output is:

    Step 1
    Database Saved
    Step 2
    

    Why?

    Because SaveCustomerAsync() isn’t waiting for Task.Delay().

    It’s waiting for the entire SaveToDatabaseAsync() method to finish.

    Only after that method completes does execution continue after its own await.

    That was my “aha!” moment.

    Finally, I understood the role of threads.

    Threads don’t travel with your method.

    They simply execute whatever work is ready.

    When an await is reached:

    • The method pauses.
    • The current thread is released.
    • The asynchronous operation continues elsewhere (such as the database or operating system).
    • When the operation finishes, an available ThreadPool thread resumes the method.

    No thread sits idle.

    No CPU cycles are wasted waiting.

    That’s why async/await improves the scalability of applications like ASP.NET Core APIs.

    Looking back, I realized something important.

    Understanding async/await isn’t about memorizing definitions.

    It’s about asking the right questions.

    Sometimes the biggest breakthrough comes from admitting:

    “I don’t actually understand this yet.”

    And that’s perfectly okay.

    Every experienced developer has been there.

    If this post clears up the same confusion I had, then sharing my learning journey was worth it.

  • How Do You Work Effectively with Cross-Functional Teams?

    How Do You Work Effectively with Cross-Functional Teams?

    Building great software isn’t just about writing clean code.

    It’s about collaborating with people from different disciplines—product managers, UI/UX designers, QA engineers, DevOps engineers, business analysts, and stakeholders.

    As a software engineer, your success depends not only on technical skills but also on how well you work with cross-functional teams.

    1. Start with a Shared Understanding

    Before writing code, make sure everyone agrees on:

    • The business goal
    • The user problem
    • The expected outcome
    • Success criteria

    When the team shares the same vision, misunderstandings are reduced.


    2. Communicate Clearly

    Avoid assuming everyone understands technical jargon.

    Instead:

    • Explain technical decisions in simple language.
    • Ask questions to clarify requirements.
    • Share updates proactively.

    Good communication prevents costly rework.


    3. Involve the Right People Early

    Don’t wait until development is complete to involve other teams.

    For example:

    • Discuss feasibility with DevOps before deployment.
    • Review designs with UI/UX before implementation.
    • Collaborate with QA while defining acceptance criteria.

    Early collaboration saves time later.


    4. Respect Different Perspectives

    Every role brings unique expertise.

    • Product Managers focus on business value.
    • Designers focus on user experience.
    • QA focuses on quality and edge cases.
    • DevOps focuses on reliability and deployment.
    • Developers focus on technical implementation.

    Listening to each perspective leads to better decisions.


    5. Handle Feedback Professionally

    Code reviews, testing feedback, and design changes are opportunities to improve—not personal criticism.

    A collaborative mindset builds trust within the team.


    6. Be Transparent About Risks

    If you identify:

    • Technical debt
    • Performance concerns
    • Security risks
    • Timeline impacts

    Raise them early.

    Surprises late in the project are much harder to manage.


    7. Focus on the Shared Goal

    Cross-functional collaboration isn’t about proving one team is right.

    It’s about delivering value to users.

    When disagreements arise, return to the shared objective and make decisions based on what’s best for the product.


    Final Thoughts

    The best software isn’t built by individual developers—it’s built by teams that communicate, collaborate, and trust one another.

    Strong technical skills may help you become a good developer.

    Strong collaboration skills help you become a great engineer.

  • Entity Framework vs Entity Framework Core: What’s the Difference?

    Entity Framework vs Entity Framework Core: What’s the Difference?

    Many developers assume Entity Framework (EF6) and Entity Framework Core (EF Core) are simply different versions of the same framework.

    They’re not.

    EF Core was redesigned from the ground up to meet the needs of modern .NET applications. While both are Object-Relational Mappers (ORMs), they differ significantly in architecture, performance, and capabilities.

    Let’s explore the key differences.


    What is Entity Framework (EF6)?

    Entity Framework 6 is the classic ORM designed primarily for the .NET Framework.

    It simplifies database access by allowing developers to work with C# objects instead of writing SQL for every operation.

    EF6 is mature, stable, and still maintained, making it a suitable choice for many existing .NET Framework applications.


    What is Entity Framework Core?

    Entity Framework Core is Microsoft’s modern ORM for .NET (formerly .NET Core).

    It was rewritten with a focus on:

    • Cross-platform support
    • High performance
    • Better extensibility
    • Modern application development
    • Cloud-native workloads

    It powers most new ASP.NET Core applications today.


    Key Differences

    FeatureEntity Framework (EF6)Entity Framework Core
    Target Platform.NET FrameworkModern .NET (.NET 6/7/8/9+)
    Cross-Platform❌ No✅ Yes
    PerformanceGoodBetter for most workloads
    LINQ TranslationMatureMore optimized and actively evolving
    Dependency InjectionLimitedBuilt-in support
    Batch OperationsLimitedImproved support
    MigrationsSupportedMore flexible and actively developed
    Cloud & ContainersLimitedDesigned with modern deployment in mind

    Performance

    EF Core generally performs better because it was designed with performance in mind.

    It includes improvements such as:

    • Faster query execution
    • Better SQL generation
    • More efficient change tracking
    • Compiled queries
    • Split queries for complex object graphs

    These features can make a noticeable difference in high-traffic applications.


    Which One Should You Choose?

    Choose EF6 if:

    • You’re maintaining an existing .NET Framework application.
    • Migrating to modern .NET isn’t currently feasible.
    • The application is stable and doesn’t require new EF Core features.

    Choose EF Core if:

    • You’re starting a new project.
    • You’re building an ASP.NET Core application.
    • You need cross-platform support.
    • Performance and scalability are important.
    • You want access to the latest features and ongoing improvements.

    Migration Considerations

    Migrating from EF6 to EF Core isn’t always a simple package upgrade.

    Some APIs and behaviors differ, and applications with heavy customizations may require code changes and testing.

    Plan the migration carefully and validate functionality before moving to production.


    Final Thoughts

    Both EF6 and EF Core are capable ORMs, but they’re designed for different generations of .NET development.

    • Maintaining a legacy .NET Framework application? EF6 is still a solid choice.
    • Building a modern application? EF Core should be your default choice.

    Technology evolves, and EF Core represents Microsoft’s long-term direction for data access in the .NET ecosystem.

  • Repository Pattern vs Unit of Work in .NET: Do You Really Need Both?

    Repository Pattern vs Unit of Work in .NET: Do You Really Need Both?

    If you’ve worked on ASP.NET Core projects, you’ve probably seen classes like:

    • CustomerRepository
    • OrderRepository
    • UnitOfWork

    But with Entity Framework Core, an important question arises:

    Do we still need Repository and Unit of Work, or is DbContext enough?

    The answer depends on your application’s complexity and architectural goals.


    What Is the Repository Pattern?

    A Repository acts as an abstraction between your business logic and the data access layer.

    Instead of writing database queries throughout your application, all data operations are centralized.

    Example:

    public interface IProductRepository
    {
        Task<Product?> GetByIdAsync(int id);
        Task AddAsync(Product product);
        Task DeleteAsync(Product product);
    }
    

    The service interacts with the repository instead of directly with Entity Framework.

    Benefits

    • Separates business logic from data access.
    • Easier to mock during unit testing.
    • Centralizes reusable queries.
    • Improves maintainability in large projects.

    What Is the Unit of Work Pattern?

    The Unit of Work coordinates multiple repositories so they participate in a single transaction.

    Instead of saving changes after every repository operation, you commit them together.

    Example:

    await unitOfWork.Products.AddAsync(product);
    await unitOfWork.Orders.AddAsync(order);
    
    await unitOfWork.SaveChangesAsync();
    

    If one operation fails, the entire transaction can be rolled back.


    Doesn’t EF Core Already Provide These?

    Yes—and this is where many developers get confused.

    DbSet<T> behaves much like a Repository

    It already provides methods such as:

    • Add()
    • Remove()
    • Find()
    • Update()

    DbContext behaves like a Unit of Work

    It:

    • Tracks entity changes.
    • Coordinates updates.
    • Executes a single SaveChanges() or SaveChangesAsync().
    • Wraps changes in a transaction when appropriate.

    Because of this, many developers consider custom Repository and Unit of Work implementations unnecessary for simple CRUD applications.


    When Should You Use Custom Repository and Unit of Work?

    They make sense when:

    • Your application is large and follows Clean Architecture or Domain-Driven Design (DDD).
    • Multiple data sources need a consistent abstraction.
    • You have complex queries reused across many services.
    • You want to isolate EF Core from the application layer.
    • You anticipate changing the data access technology in the future.

    When Might They Be Unnecessary?

    For smaller applications that use EF Core exclusively, adding generic repositories can introduce extra complexity without much benefit.

    In these cases, injecting DbContext directly into services is often simpler and perfectly acceptable.


    My Rule of Thumb

    • Small to medium CRUD applications: DbContext is usually enough.
    • Large enterprise applications: A well-designed Repository and Unit of Work layer can improve organization, testability, and maintainability.

    The important point isn’t to follow a pattern blindly—it’s to choose the level of abstraction your project actually needs.


    Final Thoughts

    Repository and Unit of Work are valuable patterns, but they’re not mandatory in every ASP.NET Core application.

    EF Core already implements many of their responsibilities.

    Before adding another abstraction layer, ask yourself:

    Does this simplify my application, or does it simply add more code to maintain?

    Good architecture isn’t about using more patterns—it’s about using the right patterns for the problem you’re solving.

  • If-Else vs Ternary Operator in C#: When Should You Use Each?

    If-Else vs Ternary Operator in C#: When Should You Use Each?

    As C# developers, we often need to make decisions in our code.

    Two common ways to do this are:

    • if-else
    • The ternary operator (?:)

    Both achieve the same goal, but choosing the right one can make your code either easier—or harder—to read.

    Let’s explore when each approach is the better choice.

    Using if-else

    The if-else statement is ideal when the logic is more than a simple decision.

    Example:

    if (age >= 18)
    {
        category = "Adult";
    }
    else
    {
        category = "Minor";
    }
    

    This style is easy to understand, especially when additional conditions or multiple statements are involved.

    When to Use if-else

    Use if-else when:

    • You have multiple statements to execute.
    • The logic is complex.
    • You need nested conditions.
    • Readability is more important than brevity.

    Using the Ternary Operator

    The ternary operator provides a shorter way to assign a value based on a condition.

    Syntax:

    condition ? valueIfTrue : valueIfFalse;
    

    Example:

    string category = age >= 18 ? "Adult" : "Minor";
    

    This is concise, expressive, and easy to read.


    A Good Use Case

    The ternary operator works well for simple value assignments.

    string status = isActive ? "Active" : "Inactive";
    

    Simple.

    Readable.

    No unnecessary lines of code.


    A Bad Use Case

    Avoid chaining multiple ternary operators.

    string result = score >= 80
        ? "Excellent"
        : score >= 60
            ? "Good"
            : score >= 40
                ? "Average"
                : "Fail";
    

    Although valid, this quickly becomes difficult to read and maintain.

    An if-else block communicates the same logic much more clearly.


    Comparison

    ScenarioRecommended
    Simple value assignment✅ Ternary Operator
    Multiple statements✅ If-Else
    Complex business logic✅ If-Else
    Nested conditions✅ If-Else
    Short, readable conditions✅ Ternary Operator

    Best Practices

    ✔ Use the ternary operator only for simple expressions.

    ✔ Prefer if-else for complex decision-making.

    ✔ Avoid deeply nested ternary operators.

    ✔ Prioritize readability over writing fewer lines of code.

    Remember:

    Code is read far more often than it is written.


    Final Thoughts

    The ternary operator is a great tool—but like any tool, it should be used appropriately.

    If it makes your code clearer, use it.

    If it makes someone stop and think, an if-else statement is probably the better choice.

    The goal isn’t to write the shortest code—it’s to write code that’s easy for your future self and your teammates to understand.

  • Creating Custom Middleware Extensions in ASP.NET Core: Keep Your Program.cs Clean

    Creating Custom Middleware Extensions in ASP.NET Core: Keep Your Program.cs Clean

    As an ASP.NET Core application grows, Program.cs can quickly become cluttered with middleware registrations.

    For example:

    app.UseMiddleware<RequestLoggingMiddleware>();
    app.UseMiddleware<ApiKeyMiddleware>();
    app.UseMiddleware<TenantMiddleware>();
    app.UseMiddleware<RequestValidationMiddleware>();
    app.UseMiddleware<MaintenanceModeMiddleware>();
    

    While this works, it’s not the cleanest approach.

    A better practice is to create extension methods that encapsulate middleware registration.


    Step 1: Create Your Middleware

    public class RequestLoggingMiddleware
    {
        private readonly RequestDelegate _next;
    
        public RequestLoggingMiddleware(RequestDelegate next)
        {
            _next = next;
        }
    
        public async Task InvokeAsync(HttpContext context)
        {
            Console.WriteLine($"Request: {context.Request.Path}");
    
            await _next(context);
    
            Console.WriteLine($"Response: {context.Response.StatusCode}");
        }
    }
    

    Step 2: Create an Extension Method

    Create a static extension class:

    public static class RequestLoggingMiddlewareExtensions
    {
        public static IApplicationBuilder UseRequestLogging(
            this IApplicationBuilder app)
        {
            return app.UseMiddleware<RequestLoggingMiddleware>();
        }
    }
    

    Now your middleware has a meaningful and reusable registration method.


    Step 3: Register It

    Instead of:

    app.UseMiddleware<RequestLoggingMiddleware>();
    

    Use:

    app.UseRequestLogging();
    

    This makes Program.cs much cleaner and easier to read.


    Why Use Extension Methods?

    ✅ Improved Readability

    Instead of exposing implementation details, your pipeline reads like documentation.

    app.UseRequestLogging();
    app.UseApiKeyValidation();
    app.UseTenantValidation();
    app.UseMaintenanceMode();
    

    ✅ Better Maintainability

    If the middleware implementation changes, the registration code remains the same.


    ✅ Reusability

    The extension method can be shared across multiple projects or packaged into a reusable library.


    ✅ Encapsulation

    Consumers don’t need to know whether you’re using:

    • UseMiddleware<T>()
    • Multiple middleware components
    • Additional configuration

    Everything is hidden behind a simple method.


    Real-World Example

    Many built-in ASP.NET Core middleware components follow this pattern:

    app.UseAuthentication();
    
    app.UseAuthorization();
    
    app.UseCors();
    
    app.UseSession();
    
    app.UseSwagger();
    

    These are all extension methods internally.

    Following the same pattern makes your custom middleware feel like a natural part of ASP.NET Core.


    Best Practices

    ✔ Keep each middleware focused on a single responsibility.

    ✔ Give extension methods meaningful names.

    ✔ Place extensions in a dedicated Extensions folder.

    ✔ Use XML comments if the middleware is part of a shared library.


    Final Thoughts

    Creating extension methods for custom middleware is a small change that greatly improves the readability and maintainability of your application.

    A clean Program.cs makes it easier for new developers to understand the request pipeline and keeps your startup configuration organized.

    When building professional ASP.NET Core applications, aim for code that is not only functional but also easy to read and maintain.

  • What If a User Uploads a Dangerous File to Your Server? Here’s How to Prevent It.

    What If a User Uploads a Dangerous File to Your Server? Here’s How to Prevent It.

    File upload is one of the most common features in web applications—but it’s also one of the most targeted by attackers.

    Imagine a user uploads a file named:

    invoice.pdf.exe
    

    Or renames a malicious executable to:

    resume.pdf
    

    If your application trusts only the file name or extension, you could expose your server to serious security risks.

    A secure file upload system should never rely on a single validation step.

    1. Validate the File Extension

    Allow only the file types your application actually needs.

    Example:

    • .pdf
    • .jpg
    • .png
    • .docx

    Reject everything else.

    Remember: This is your first line of defense—not your only one.


    2. Verify the MIME Type

    Don’t trust the extension alone.

    Check the file’s MIME type sent by the client.

    For example:

    • application/pdf
    • image/jpeg
    • image/png

    Keep in mind that MIME types can also be spoofed, so continue with additional checks.


    3. Validate the File Signature (Magic Numbers)

    The most reliable validation is checking the file’s binary signature.

    For example:

    • PDF → %PDF
    • PNG → 89 50 4E 47
    • JPEG → FF D8 FF

    If the file signature doesn’t match the expected format, reject the upload.


    4. Limit File Size

    Prevent attackers from uploading extremely large files.

    Example:

    • Images: 5 MB
    • Documents: 20 MB

    This reduces the risk of storage abuse and denial-of-service attacks.


    5. Rename Uploaded Files

    Never store files using the original filename.

    Instead, generate a unique name.

    Example:

    3b8b2d1d-ef7d-4db2-8a8c-0f5d91a0f4a7.pdf
    

    This avoids filename collisions and reduces information disclosure.


    6. Store Files Outside the Web Root

    Avoid storing uploaded files in directories that can execute code.

    Instead:

    • Store files outside the web root.
    • Serve them through a controller or API after authorization.

    This prevents direct execution of uploaded files.


    7. Scan for Malware

    Integrate an antivirus solution to scan uploaded files before making them available.

    This is especially important for systems that accept documents from external users.


    8. Restrict File Permissions

    Uploaded files should never have execute permissions.

    Grant only the minimum permissions required to read or write the file.


    9. Authorize Access

    Not every uploaded file should be publicly accessible.

    Always verify that the requesting user has permission to download or view the file.


    10. Log Upload Activity

    Record important details such as:

    • User ID
    • IP address
    • File name
    • File size
    • Upload time
    • Validation failures

    Logs help detect suspicious activity and support incident investigations.


    Final Thoughts

    Secure file uploads are about defense in depth.

    Don’t rely on a single validation.

    A robust upload pipeline should include:

    • Extension validation
    • MIME type verification
    • File signature checks
    • Size limits
    • Malware scanning
    • Secure storage
    • Proper authorization

    Security isn’t a single feature—it’s a combination of small decisions that work together to protect your application.

  • User বারবার Send Button চাপল, আর Customer-এর টাকা দুইবার কেটে গেল! এটা কীভাবে প্রতিরোধ করবেন?

    User বারবার Send Button চাপল, আর Customer-এর টাকা দুইবার কেটে গেল! এটা কীভাবে প্রতিরোধ করবেন?

    ধরুন, আপনি একটি Payment API তৈরি করেছেন।

    একজন User “Pay Now” বাটনে চাপ দিলেন।

    কিন্তু Server একটু ধীর (Slow)।

    User ভাবলেন,

    “হয়তো Click হয়নি!”

    তিনি আবার Button চাপলেন।

    তারপর আবার।

    কয়েক সেকেন্ড পরে…

    Customer Support থেকে ফোন—

    “একই Payment-এর জন্য Customer-এর টাকা দুইবার কেটে গেছে!”

    এখন প্রশ্ন হলো—

    সমস্যাটা User-এর, নাকি System-এর?

    আমার মতে, System-এর।

    কারণ একটি ভালো Payment System কখনোই একই Payment একাধিকবার Process করবে না।


    প্রথম প্রতিরক্ষা: Frontend

    Frontend থেকেই Duplicate Request কমানো যায়।

    যেমন—

    • Button Click করার পর Disable করে দেওয়া।
    • Loading Spinner দেখানো।
    • Request Complete না হওয়া পর্যন্ত দ্বিতীয় Click Block করা।

    এতে অনেক Duplicate Request শুরুতেই বন্ধ হয়ে যায়।

    কিন্তু…

    শুধু Frontend-এর উপর নির্ভর করা যাবে না।

    কারণ—

    • User Page Refresh করতে পারে।
    • Network Retry হতে পারে।
    • Mobile App একই Request আবার পাঠাতে পারে।
    • কোনো Bot Request পাঠাতে পারে।

    তাই আসল নিরাপত্তা Backend-এ থাকতে হবে।


    দ্বিতীয় প্রতিরক্ষা: Idempotency Key

    প্রতিটি Payment Request-এর সাথে একটি Unique Id পাঠানো হয়।

    যেমন—

    Idempotency-Key: 7f4c9d...
    

    যদি একই Key আবার আসে,

    Server নতুন Payment Process করবে না।

    আগের Result-ই Return করবে।


    তৃতীয় প্রতিরক্ষা: Database Constraint

    Database-এ এমনভাবে Design করুন,

    যাতে একই Transaction Reference দুইবার Insert-ই করা না যায়।

    যেমন—

    • PaymentReference
    • OrderId
    • TransactionId

    এসবের উপর Unique Constraint থাকতে পারে।

    Database অনেক সময় শেষ নিরাপত্তা দেয়।


    চতুর্থ প্রতিরক্ষা: Transaction

    Payment-এর গুরুত্বপূর্ণ Operation-গুলো Transaction-এর মধ্যে করুন।

    যাতে মাঝপথে কোনো সমস্যা হলে Partial Data Save না হয়।


    পঞ্চম প্রতিরক্ষা: Distributed Lock (যেখানে প্রয়োজন)

    যদি Multiple Server একই Payment Process করতে পারে,

    তাহলে Redis Distributed Lock-এর মতো সমাধান ব্যবহার করা যেতে পারে।

    এতে একই সময়ে একটি Payment শুধুমাত্র একটি Server Process করবে।


    সবচেয়ে গুরুত্বপূর্ণ শিক্ষা

    একজন User একই Request একাধিকবার পাঠাতে পারেন।

    Network একই Request Retry করতে পারে।

    Gateway একই Callback আবার পাঠাতে পারে।

    এসবই স্বাভাবিক।

    অস্বাভাবিক হলো—

    System সেই Request-গুলোকে নতুন Payment হিসেবে ধরে আবার টাকা কেটে ফেলছে।

    একটি ভালো Payment System-এর লক্ষ্য হওয়া উচিত—

    “One business operation = One successful payment.”

    যতবার Request আসুক না কেন।

  • ম্যানেজার হঠাৎ বললেন, “এই Project-এর Estimation করে দেন!” — তখন কী করবেন?

    ম্যানেজার হঠাৎ বললেন, “এই Project-এর Estimation করে দেন!” — তখন কী করবেন?

    সকালের Stand-up Meeting শেষ।

    হঠাৎ ম্যানেজার বললেন,

    “Faiz, এই নতুন Project-এর একটা Estimation আজকেই লাগবে।”

    প্রথম প্রতিক্রিয়া কী হবে?

    অনেকেই তাড়াহুড়ো করে একটা সংখ্যা বলে দেন—

    “মনে হয়… ২ সপ্তাহ লাগবে।”

    কিন্তু একজন অভিজ্ঞ Software Engineer জানেন,

    Estimation মানে Guess করা নয়।

    Estimation মানে হলো, বর্তমান তথ্যের ভিত্তিতে একটি যৌক্তিক পূর্বাভাস (Forecast)।


    আমি কীভাবে শুরু করি?

    প্রথমেই আমি একটি প্রশ্ন করি—

    Requirement কি পুরোপুরি পরিষ্কার?

    যদি Requirement পরিষ্কার না হয়, তাহলে কোনো Estimation-ই নির্ভুল হবে না।


    এরপর Project-টাকে ছোট ছোট অংশে ভাগ করি

    একটি বড় Feature-এর Estimation করা কঠিন।

    কিন্তু যদি এটিকে ভাগ করি—

    • Authentication
    • Database Design
    • API Development
    • Frontend Integration
    • Unit Testing
    • Deployment

    তাহলে Estimation অনেক বাস্তবসম্মত হয়।


    Unknown বিষয়গুলো আলাদা করি

    সব Project-এই কিছু Unknown থাকে।

    যেমন—

    • Third-party API Integration
    • নতুন Technology
    • Client-এর পরিবর্তিত Requirement

    এসব ঝুঁকি (Risk) আগে থেকেই চিহ্নিত করলে Estimation আরও বাস্তবসম্মত হয়।


    Buffer রাখতে ভুলবেন না

    একটি Project কখনোই শুধুমাত্র Coding নয়।

    এর সাথে থাকে—

    • Code Review
    • Bug Fix
    • Testing
    • Meeting
    • Deployment
    • Production Support

    শুধু Coding Time ধরে Estimation করলে প্রায়ই Deadline মিস হয়।


    সবচেয়ে বড় ভুল

    অনেক সময় আমরা Pressure-এর কারণে বলি—

    “হ্যাঁ, হয়ে যাবে।”

    কিন্তু বাস্তবে Requirement না বুঝে দেওয়া Estimation পুরো Team-এর জন্য সমস্যার কারণ হতে পারে।

    একজন Professional Engineer প্রয়োজনে বলেন—

    “আমি Requirement Review করে আজ বিকেলের মধ্যে একটি বাস্তবসম্মত Estimation জানাচ্ছি।”

    এটা দুর্বলতা নয়, বরং Professionalism।


    আমার শেখা একটি বিষয়

    বছরের পর বছর কাজ করে একটি বিষয় বুঝেছি—

    ভুল Estimation দেওয়ার চেয়ে, একটু সময় নিয়ে সঠিক Estimation দেওয়া অনেক ভালো।

    কারণ Deadline মিস করলে শুধু Project নয়, Team-এর বিশ্বাসও ক্ষতিগ্রস্ত হয়।

    শেষ কথা

    Estimation কোনো প্রতিশ্রুতি (Promise) নয়।

    এটি হলো একটি Forecast, যা বর্তমান তথ্যের উপর ভিত্তি করে তৈরি হয়।

    আর একজন ভালো Software Engineer-এর কাজ শুধু দ্রুত উত্তর দেওয়া নয়, বরং তথ্যভিত্তিক এবং বাস্তবসম্মত উত্তর দেওয়া।

  • SQL UPDATE: Best Practices Every Developer Should Know

    SQL UPDATE: Best Practices Every Developer Should Know

    The UPDATE statement is one of the most frequently used SQL commands.

    It’s also one of the most dangerous.

    A single mistake can modify thousands—or even millions—of rows in seconds.

    That’s why every developer should understand not just how to use UPDATE, but how to use it safely.

    Basic Syntax

    UPDATE Employees
    SET Salary = 60000
    WHERE EmployeeId = 101;
    

    This updates only the employee with ID 101.


    The Most Dangerous Mistake

    Imagine running this query:

    UPDATE Employees
    SET Salary = 60000;
    

    Notice what’s missing?

    The WHERE clause.

    This statement updates every row in the table.

    In production, this mistake can be disastrous.


    Always Verify Before Updating

    Before executing an UPDATE, run the same condition with a SELECT.

    Instead of this:

    UPDATE Employees
    SET Salary = 60000
    WHERE Department = 'HR';
    

    Verify the affected rows first:

    SELECT *
    FROM Employees
    WHERE Department = 'HR';
    

    If the SELECT returns the expected rows, you’re much safer executing the update.


    Use Transactions for Large Updates

    For critical changes, wrap your update in a transaction.

    BEGIN TRANSACTION;
    
    UPDATE Employees
    SET Salary = Salary * 1.10
    WHERE Department = 'HR';
    
    -- Verify the results
    
    COMMIT;
    
    -- Or rollback if something looks wrong
    -- ROLLBACK;
    

    Transactions give you a chance to validate the changes before making them permanent.


    Update Only What You Need

    Avoid unnecessary updates.

    Instead of updating every column:

    UPDATE Employees
    SET
        Name = 'John',
        Salary = 60000,
        Department = 'IT';
    

    Update only the columns that actually changed.

    This reduces unnecessary writes and can improve performance.


    Be Careful with Joins

    SQL Server allows updates using joins.

    Example:

    UPDATE e
    SET e.ManagerId = d.ManagerId
    FROM Employees e
    JOIN Departments d
    ON e.DepartmentId = d.Id;
    

    Always verify the join with a SELECT first to ensure it returns the expected rows.


    Performance Tips

    ✔ Ensure the WHERE column is indexed.

    ✔ Avoid updating millions of rows in a single transaction when possible.

    ✔ Batch large updates to reduce locking and transaction log growth.

    ✔ Review the execution plan for expensive updates.


    Best Practices Checklist

    • ✅ Always use a WHERE clause unless you intentionally want to update every row.
    • ✅ Run a SELECT first to verify the affected rows.
    • ✅ Use transactions for critical updates.
    • ✅ Update only the required columns.
    • ✅ Test large updates in a non-production environment first.

    Final Thoughts

    Writing an UPDATE statement is easy.

    Writing a safe UPDATE statement is what separates experienced developers from beginners.

    A few extra seconds spent verifying your query can prevent hours of recovery work later.