Skip to main content

Command Palette

Search for a command to run...

তোমার Project-এ Coupled Code কীভাবে খুঁজে বের করবে?

Published
10 min read
I

A highly motivated and experienced full-stack developer with a proven track record of developing and deploying web applications. Skilled in a range of programming languages and frameworks, as well as database technologies. Comfortable working in a fast-paced environment and able to adapt to new technologies quickly. A team player who is also able to work independently when required.

Coupled code খোঁজা মানে হচ্ছে তোমার codebase-এ এমন জায়গা খুঁজে বের করা যেখানে একটা অংশ আরেকটার উপর বেশি depend করছে। এটা একটা detective work — তুমি clue খুঁজবে, pattern দেখবে, এবং সমস্যা চিহ্নিত করবে।

চলো step by step শিখি কীভাবে এটা করতে হয়।


কেন Coupled Code খোঁজা দরকার?

প্রথমে বুঝি কেন এটা important:

Coupled code-এর সমস্যা:

  • একটা জায়গায় change করলে অন্য ১০ টা জায়গায় bug হয়

  • Testing করা কঠিন হয়ে যায়

  • নতুন feature যোগ করতে গেলে পুরো system নাড়াচাড়া করতে হয়

  • Code maintain করা nightmare হয়ে যায়

তাই early detection করা better — যত আগে ধরবে, তত সহজে fix করতে পারবে।


Signal #1: "new" Keyword সরাসরি ইউজ হচ্ছে

কী খুঁজবে?

Function বা method-এর ভিতরে যদি সরাসরি concrete type create করা হয়, সেটা coupling-এর sign।

Example: Coupled Code

func ProcessOrder(orderID string) {
    // এখানে সমস্যা: সরাসরি MySQL create করছে
    db := &MySQL{
        host: "localhost",
        port: 3306,
    }

    order := db.GetOrder(orderID)
    fmt.Println(order)
}

কেন এটা সমস্যা?

  • ProcessOrder() শুধু MySQL-এর সাথেই কাজ করবে

  • PostgreSQL বা MongoDB দিতে চাইলে পুরো function rewrite করতে হবে

  • Test-এ fake database দেওয়া impossible

কীভাবে খুঁজবে?

তোমার code editor-এ search করো:

  • new(

  • &TypeName{

  • Constructor calls (NewMySQL(), CreateConnection())

Red flag: যদি এগুলো function/method-এর ভিতরে থাকে (global level বা main function ছাড়া)।


Signal #2: Import Statement-এ Concrete Package

কী খুঁজবে?

High-level package যদি low-level concrete package import করে, সেটা tight coupling।

Example: Coupled Structure

package service

import (
    "myapp/database/mysql"    // ⚠️ Concrete implementation import
    "myapp/cache/redis"       // ⚠️ Concrete implementation import
)

type UserService struct {
    db    *mysql.MySQL        // ⚠️ Concrete type dependency
    cache *redis.Redis        // ⚠️ Concrete type dependency
}

কেন এটা সমস্যা?

  • UserService জানে MySQL এবং Redis-এর internal details

  • Database বদলাতে গেলে UserService change করতে হবে

  • এই package অন্য project-এ reuse করা impossible

কীভাবে খুঁজবে?

১. তোমার business logic file গুলো open করো ২. Import section দেখো ৩. Question করো: "এই package কি implementation details জানা উচিত?"

Red flag patterns:

import "myapp/database/mysql"
import "myapp/storage/s3" 
import "myapp/payment/stripe"
import "myapp/email/sendgrid"

এগুলো সব concrete implementation। Business logic-এ এদের direct import থাকা উচিত না।


Signal #3: Type Assertion বারবার ব্যবহার

কী খুঁজবে?

যদি তুমি বারবার .(Type) type assertion করছো, মানে তোমার abstraction ঠিকমতো কাজ করছে না।

Example: Coupled Code

func ProcessStorage(s interface{}) {
    // ⚠️ Concrete type check করতে হচ্ছে
    if db, ok := s.(*MySQL); ok {
        db.Connect()
        db.Query("SELECT * FROM users")
    } else if file, ok := s.(*FileStorage); ok {
        file.Open()
        file.Read()
    }
    // আরো type check...
}

কেন এটা সমস্যা?

  • নতুন storage type যোগ করতে গেলে এই function modify করতে হবে

  • Open/Closed Principle violate করছে

  • Interface제대로 design করা হয়নি

ভালো Alternative

type Storage interface {
    Initialize() error
    Fetch(query string) ([]byte, error)
}

func ProcessStorage(s Storage) {
    s.Initialize()
    data, _ := s.Fetch("users")
    fmt.Println(data)
}

কীভাবে খুঁজবে?

Search করো:

  • .( — type assertion

  • switch v := x.(type) — type switch

Red flag: যদি same function-এ multiple concrete types check করা হয়।


Signal #4: Testing-এ অসুবিধা

কী খুঁজবে?

যদি unit test লিখতে গিয়ে এসব সমস্যা হয়, তাহলে coupling আছে:

Example: Test করতে পারছো না

// এই function test করতে চাও
func CreateUser(name string) error {
    db := &MySQL{host: "production.db.com"}  // ⚠️ Production DB!
    return db.Insert("users", name)
}

// Test লিখতে গেলে...
func TestCreateUser(t *testing.T) {
    // সমস্যা: Real production database-এ connect হবে!
    // Fake database inject করার কোনো উপায় নেই
    err := CreateUser("Ahmed")
    // এই test run করা dangerous
}

Signs of Coupling in Tests:

১. External dependency দরকার পড়ে:

  • Real database setup করতে হয়

  • Real API call করতে হয়

  • File system access লাগে

২. Test slow হয়ে যায়:

  • Network calls

  • Database queries

  • File I/O operations

৩. Test brittle হয়:

  • একটা service down থাকলে test fail করে

  • Environment-এর উপর depend করে

কীভাবে খুঁজবে?

১. Existing tests দেখো:

  • Setup code লম্বা হয়ে যাচ্ছে?

  • External services mock করা হচ্ছে?

  • Tests কি slow?

২. যেখানে test নেই, সেখানে লিখতে চেষ্টা করো:

  • Dependency inject করতে পারছো?

  • Mock বানাতে পারছো?

  • Isolated test possible?

Red flag: যদি test লিখতে গিয়ে whole system setup করতে হয়।


Signal #5: একই Code বারবার Copy-Paste

কী খুঁজবে?

Similar code different জায়গায় repeat হচ্ছে — তবে slightly different concrete types দিয়ে।

Example: Duplication due to Coupling

// MySQL-এর জন্য
func SaveUserToMySQL(user User) {
    db := &MySQL{}
    db.Connect()
    db.Execute("INSERT INTO users...")
}

// PostgreSQL-এর জন্য  
func SaveUserToPostgres(user User) {
    db := &Postgres{}
    db.Connect()
    db.Execute("INSERT INTO users...")
}

// MongoDB-এর জন্য
func SaveUserToMongo(user User) {
    db := &MongoDB{}
    db.Connect()
    db.Execute("INSERT INTO users...")
}

কেন এটা সমস্যা?

  • একই logic তিনবার লেখা হচ্ছে

  • Bug fix করতে গেলে তিন জায়গায় fix করতে হবে

  • নতুন database যোগ করতে গেলে আরেকটা function লিখতে হবে

ভালো Alternative

type Database interface {
    Connect() error
    Execute(query string) error
}

func SaveUser(user User, db Database) {
    db.Connect()
    db.Execute("INSERT INTO users...")
}

কীভাবে খুঁজবে?

১. Similar function names খুঁজো:

  • ProcessWithMySQL() এবং ProcessWithPostgres()

  • SendEmailViaSMTP() এবং SendEmailViaAPI()

২. Code diff tools ইউজ করো:

  • দুটো file পাশাপাশি রাখো

  • দেখো structure same কিনা, শুধু type names আলাদা

Red flag: একই pattern বারবার, শুধু concrete types different।


Signal #6: অনেক Parameters Function-এ

কী খুঁজবে?

Function যদি অনেকগুলো concrete dependencies parameter হিসেবে নেয়।

Example: Parameter Hell

func ProcessOrder(
    mysqlDB *MySQL,
    redisCache *Redis,
    stripePayment *Stripe,
    sendgridEmail *Sendgrid,
    s3Storage *S3,
    orderID string,
) error {
    // অনেকগুলো concrete dependencies
}

কেন এটা সমস্যা?

  • Function signature বদলালে everywhere update করতে হবে

  • Testing-এ সব dependencies mock করতে হবে

  • নতুন dependency যোগ করা painful

ভালো Alternative

type OrderDependencies struct {
    DB      Database
    Cache   Cache
    Payment PaymentGateway
    Email   EmailService
    Storage Storage
}

func ProcessOrder(deps OrderDependencies, orderID string) error {
    // Clean and extensible
}

কীভাবে খুঁজবে?

১. Function signatures দেখো ২. Count করো: 3+ pointer parameters আছে? ৩. Check করো: সব parameters কি concrete types?

Red flag: 4+ concrete type parameters।


Signal #7: "God Object" বা বড় Struct

কী খুঁজবে?

একটা struct যেটা অনেক কিছু জানে এবং অনেক কিছু করে।

Example: God Object

type Application struct {
    mysqlDB       *MySQL
    postgresDB    *Postgres
    redisCache    *Redis
    s3Storage     *S3
    stripePayment *Stripe
    twilioSMS     *Twilio
    sendgridEmail *Sendgrid
    // আরো 20+ fields...
}

func (app *Application) DoEverything() {
    // 500 lines of code
}

কেন এটা সমস্যা?

  • এক জায়গায় too many responsibilities

  • Change করতে গেলে impact analysis কঠিন

  • Reuse করা impossible

  • Testing nightmare

কীভাবে খুঁজবে?

১. Struct definitions খুঁজো যেখানে 10+ fields ২. Methods খুঁজো যেখানে 100+ lines ৩. Check করো: একটা struct কি multiple concerns handle করছে?

Red flags:

  • UserAuthPaymentEmailHandler (multiple responsibilities in name)

  • 500+ line methods

  • Struct-এ 15+ dependencies


Signal #8: Circular Dependencies

কী খুঁজবে?

Package A imports Package B, এবং Package B imports Package A — এটা circular dependency।

Example: Circular Import

package user
import "myapp/order"

type User struct {
    Orders []order.Order  // user depends on order
}
package order  
import "myapp/user"

type Order struct {
    User user.User  // order depends on user
}

কেন এটা সমস্যা?

  • Go-তে compile-ই হবে না!

  • Design fundamentally flawed

  • Strong coupling-এর ultimate sign

কীভাবে খুঁজবে?

Go automatically detect করবে:

package myapp/user
    imports myapp/order
    imports myapp/user: import cycle not allowed

Solution approach:

  • Common interface আলাদা package-এ রাখো

  • Dependency direction ঠিক করো (high-level → low-level)

  • Domain boundaries properly define করো


Practical Exercise: তোমার Project Analyze করো

Step 1: Quick Scan (10 minutes)

Terminal-এ এই commands run করো:

# 1. Check করো কোন files সবচেয়ে বড়
find . -name "*.go" -exec wc -l {} \; | sort -rn | head -10

# 2. "new" keyword কতবার ইউজ হয়েছে
grep -r "new(" . --include="*.go" | wc -l

# 3. Type assertions খুঁজো
grep -r "\.\(" . --include="*.go" | head -20

# 4. Import statements analyze করো
grep -r "^import" . --include="*.go" | grep "database\|mysql\|postgres"

Step 2: Manual Review (30 minutes)

১. সবচেয়ে বড় file open করো:

  • Structure দেখো

  • Dependencies count করো

  • একটা function কতগুলো concrete types ইউজ করছে?

২. Main business logic file খুঁজো:

  • service/, handler/, controller/ folders দেখো

  • Import statements analyze করো

  • Concrete types সরাসরি ইউজ হচ্ছে?

৩. Test files দেখো:

  • Test setup কতটা complicated?

  • Mock করা হচ্ছে কিনা?

  • Test coverage কম কেন?

Step 3: Create a Coupling Report

একটা document বানাও:

# Coupling Analysis Report

## High Priority Issues
1. UserService directly creates MySQL connection
   - File: service/user.go:45
   - Impact: Cannot test without real DB
   - Solution: Inject Database interface

2. PaymentHandler imports stripe package
   - File: handler/payment.go:12
   - Impact: Tied to one payment provider
   - Solution: Create PaymentGateway interface

## Medium Priority
...

## Low Priority
...

Coupling Score Calculator

তোমার codebase-এর coupling level measure করার একটা সহজ formula:

Coupling Score = 
  (Direct Type Creation × 3) +
  (Concrete Package Imports × 2) +
  (Type Assertions × 2) +
  (Large Structs with 10+ fields × 1) +
  (Functions with 5+ params × 1)

Scoring:

  • 0-20: ভালো! Low coupling

  • 21-50: Okay, improvement সম্ভব

  • 51-100: Medium coupling, refactor করা উচিত

  • 100+: High coupling, serious refactoring দরকার


Automated Tools ইউজ করো

1. Go Vet

Built-in tool, basic issues ধরে:

go vet ./...

2. Golint

Code style এবং best practices check করে:

go install golang.org/x/lint/golint@latest
golint ./...

3. Go-Critic

Advanced linter, coupling patterns detect করে:

go install github.com/go-critic/go-critic/cmd/gocritic@latest
gocritic check ./...

4. Staticcheck

Comprehensive static analysis:

go install honnef.co/go/tools/cmd/staticcheck@latest
staticcheck ./...

Visual Dependency Analysis

Package Dependency Graph বানাও

# godepgraph install করো
go install github.com/kisielk/godepgraph@latest

# Dependency graph generate করো
godepgraph -s -o myapp | dot -Tpng -o dependencies.png

এই graph দেখলে বুঝবে:

  • কোন packages সবচেয়ে বেশি imported

  • Circular dependencies আছে কিনা

  • Dependency direction ঠিক আছে কিনা


একটা Real Example: Refactoring Journey

Before: Highly Coupled

package main

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
)

type UserHandler struct{}

func (h UserHandler) CreateUser(name, email string) error {
    // ⚠️ Direct MySQL connection
    db, err := sql.Open("mysql", "user:pass@tcp(localhost:3306)/db")
    if err != nil {
        return err
    }
    defer db.Close()

    // ⚠️ Direct SQL query
    _, err = db.Exec("INSERT INTO users (name, email) VALUES (?, ?)", name, email)
    return err
}

Problems identified:

  1. Handler directly creates database connection

  2. Testing impossible without real MySQL

  3. Cannot switch databases

  4. SQL logic mixed with handler logic

After: Decoupled

package main

// Interface defines behavior
type UserRepository interface {
    Create(name, email string) error
}

// Handler depends on interface
type UserHandler struct {
    repo UserRepository
}

func NewUserHandler(repo UserRepository) *UserHandler {
    return &UserHandler{repo: repo}
}

func (h *UserHandler) CreateUser(name, email string) error {
    return h.repo.Create(name, email)
}

// Concrete implementation in separate package
type MySQLUserRepository struct {
    db *sql.DB
}

func (r *MySQLUserRepository) Create(name, email string) error {
    _, err := r.db.Exec("INSERT INTO users (name, email) VALUES (?, ?)", name, email)
    return err
}

// Test-এর জন্য mock
type MockUserRepository struct {
    CreateFunc func(name, email string) error
}

func (m *MockUserRepository) Create(name, email string) error {
    return m.CreateFunc(name, email)
}

Benefits: ✅ Handler testable with mock repository
✅ Can switch from MySQL to Postgres easily
✅ Clear separation of concerns
✅ Follows dependency inversion principle


Action Plan: কী করবে এখন?

Week 1: Discovery

  • [ ] তোমার codebase scan করো

  • [ ] Top 5 coupled areas identify করো

  • [ ] Coupling report লিখো

Week 2: Small Wins

  • [ ] সবচেয়ে ছোট coupled function refactor করো

  • [ ] Interface extract করো

  • [ ] Test লিখো

Week 3: Medium Refactoring

  • [ ] একটা service layer decouple করো

  • [ ] Repository pattern implement করো

  • [ ] Integration tests add করো

Week 4: Review & Document

  • [ ] Coupling score আবার measure করো

  • [ ] Improvement document করো

  • [ ] Team-এর সাথে share করো


শেষ কথা

Coupled code খোঁজা একটা skill যেটা practice-এর সাথে develop হয়। শুরুতে কঠিন মনে হতে পারে, কিন্তু এই signals মাথায় রাখলে ধীরে ধীরে pattern চিনতে শুরু করবে।

মনে রাখো:

  1. Perfect code খোঁজো না — improvement খোঁজো

  2. একবারে সব fix করতে যেও না — incremental refactoring করো

  3. Tests লিখো — refactoring-এর আগে এবং পরে

  4. Document করো — কেন change করলে সেটা লিখো

Coupling detect করা হলো প্রথম step। Decoupling করা হবে পরের journey।

Happy refactoring!

More from this blog

Low Level Design: গভীর থেকে বোঝা এবং আয়ত্ত করা

ভূমিকা: কেন এই Article? তুমি হয়তো programming শিখেছ। Variable, loop, function, data structure - সব জানো। কিন্তু যখন একটা বড় system বানাতে বসো, তখন মনে হয় কোথা থেকে শুরু করব? কীভাবে organize করব? Code লিখতে লিখতে হারিয়ে যাও একটা maze-এ। এই feeling...

Oct 15, 202520 min read71
Low Level Design: গভীর থেকে বোঝা এবং আয়ত্ত করা

Go-তে Interface কীভাবে Code Decouple করে?

একটা HTTP Server দিয়ে পুরো ব্যাপারটা বুঝে নেওয়া যাক আমরা সবাই জানি Go একটা সিম্পল ল্যাঙ্গুয়েজ, কিন্তু interface নিয়ে অনেকেরই confusion থাকে। আজকে আমরা দেখব কীভাবে interface আসলে তোমার code-কে flexible এবং maintainable বানায়। একটা real-world HTT...

Oct 14, 202520 min read5
Go-তে Interface কীভাবে Code Decouple করে?

Go-তে Object (Struct Instance) তৈরির সম্পূর্ণ গাইড

Go programming শেখার সময় একটা জিনিস খুব তাড়াতাড়ি বুঝতে হয় - কীভাবে object তৈরি করতে হয়। অন্য language যেমন Java বা Python এ class আছে, কিন্তু Go-তে আছে struct। আর struct এর instance বানানোই হলো object তৈরি করা। আজকের এই blog এ আমরা দেখব Go-তে ob...

Oct 13, 202524 min read3
Go-তে Object (Struct Instance) তৈরির সম্পূর্ণ গাইড
I

Imran Hasan

61 posts

Full-stack developer with experience in developing and managing web applications. Skilled in React, Node.js, HTML, CSS, and JavaScript. Experience in managing website hosting and security.