DEV Community

Arbythecoder
Arbythecoder

Posted on

What I Wish I Knew Before Building My First Client Website

What You'll Learn

  • How to avoid the "jack-of-all-trades" trap that kills projects
  • Payment integration strategies that actually work
  • Why backend fundamentals matter more than trendy frameworks
  • Practical tips for managing international clients
  • Code examples and tools that save time and headaches

When I landed my first paying client, I thought I had it all figured out.

Spoiler alert: I didn't.

Here's what I learned the hard way (so you don't have to).

❌ Mistake #1: Trying to Be Everything to Everyone

What I Promised:

✅ Frontend Development
✅ Backend Development  
✅ UI/UX Design
✅ Database Architecture
✅ Payment Integration
✅ Project Management
Enter fullscreen mode Exit fullscreen mode

What I Could Actually Do:

✅ Backend Development (sort of)
❌ Everything else (badly)
Enter fullscreen mode Exit fullscreen mode

The Reality Check:
I spent 3 weeks on a design that a proper designer could have done in 3 days.

💡 Practical Tip:
Pick ONE thing you're good at. Master it. Then expand.

Quick Specialization Checklist:

  • [ ] Can you build this feature without YouTube tutorials?
  • [ ] Do you understand the underlying concepts?
  • [ ] Can you debug it when it breaks?
  • [ ] Would you trust this code in production?

If you answered "no" to any of these, you're not ready to charge for it.

❌ Mistake #2: Payment Integration Nightmare

The Problem:

Client: "Can users pay through the site?"
Me: "Of course!" (I had never done this before)

What Happened:

  • Account blocked by payment processors (twice!)
  • Test transactions failing randomly
  • No idea about PCI compliance
  • Webhooks? What are those?

The Solution:

Start simple, then get complex.

Beginner-Friendly Payment Options:

// Start with Stripe Checkout (hosted solution)
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

app.post('/create-checkout-session', async (req, res) => {
  try {
    const session = await stripe.checkout.sessions.create({
      payment_method_types: ['card'],
      line_items: [{
        price_data: {
          currency: 'usd',
          product_data: {
            name: 'Website Service',
          },
          unit_amount: 2000, // $20.00
        },
        quantity: 1,
      }],
      mode: 'payment',
      success_url: 'https://yourdomain.com/success',
      cancel_url: 'https://yourdomain.com/cancel',
    });

    res.json({ id: session.id });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});
Enter fullscreen mode Exit fullscreen mode

⚠️ Always Use Sandbox First:

# Test with Stripe CLI
stripe listen --forward-to localhost:3000/webhook
Enter fullscreen mode Exit fullscreen mode

Payment Integration Checklist:

  • [ ] Test in sandbox environment
  • [ ] Handle failed payments gracefully
  • [ ] Set up proper error logging
  • [ ] Understand webhook security
  • [ ] Know your compliance requirements

❌ Mistake #3: Weak Backend Fundamentals

The Problem:

I jumped to Node.js because it was trendy, but I didn't understand:

  • How databases actually work
  • Authentication vs authorization
  • API design principles
  • Error handling strategies
  • Security best practices

The Wake-Up Call:

// My first "secure" login endpoint 😅
app.post('/login', (req, res) => {
  const { username, password } = req.body;

  // DON'T DO THIS!
  const query = `SELECT * FROM users WHERE username = '${username}' AND password = '${password}'`;

  // This is vulnerable to SQL injection!
  db.query(query, (err, results) => {
    if (results.length > 0) {
      res.json({ message: 'Login successful' });
    } else {
      res.json({ message: 'Invalid credentials' });
    }
  });
});
Enter fullscreen mode Exit fullscreen mode

The Better Approach:

// Proper authentication with hashing and prepared statements
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');

app.post('/login', async (req, res) => {
  try {
    const { username, password } = req.body;

    // Use prepared statements to prevent SQL injection
    const query = 'SELECT * FROM users WHERE username = ?';
    const results = await db.query(query, [username]);

    if (results.length === 0) {
      return res.status(401).json({ error: 'Invalid credentials' });
    }

    const user = results[0];
    const validPassword = await bcrypt.compare(password, user.password);

    if (!validPassword) {
      return res.status(401).json({ error: 'Invalid credentials' });
    }

    const token = jwt.sign(
      { userId: user.id, username: user.username },
      process.env.JWT_SECRET,
      { expiresIn: '24h' }
    );

    res.json({ token, user: { id: user.id, username: user.username } });
  } catch (error) {
    console.error('Login error:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
});
Enter fullscreen mode Exit fullscreen mode

Backend Fundamentals Checklist:

  • [ ] Understand database relationships
  • [ ] Know the difference between authentication and authorization
  • [ ] Can implement proper error handling
  • [ ] Understand security basics (hashing, HTTPS, input validation)
  • [ ] Know how to structure APIs properly

💡 Working with International Clients

The Challenges:

  • Time zone differences
  • Payment method compatibility
  • Cultural communication styles
  • Currency conversion headaches

Practical Solutions:

Time Zone Management:

// Use UTC for all database timestamps
const moment = require('moment-timezone');

const createEvent = (eventData, clientTimezone) => {
  return {
    ...eventData,
    // Store in UTC
    createdAt: moment().utc().toISOString(),
    // Display in client's timezone
    displayTime: moment().tz(clientTimezone).format('YYYY-MM-DD HH:mm')
  };
};
Enter fullscreen mode Exit fullscreen mode

Payment Solutions by Region:

  • US/Canada: Stripe, Square
  • Europe: Stripe, PayPal
  • Asia: PayPal, local payment gateways
  • Africa: Flutterwave, Paystack

International Client Checklist:

  • [ ] Agree on communication schedule upfront
  • [ ] Use project management tools with timezone support
  • [ ] Research local payment preferences
  • [ ] Have backup payment methods ready
  • [ ] Understand basic cultural communication differences

🛠️ Tools That Save Your Sanity

Project Management:

# Essential tools for client projects
npm install -g @vercel/cli  # Easy deployment
npm install helmet express-rate-limit  # Security
npm install dotenv  # Environment variables
npm install winston  # Proper logging
Enter fullscreen mode Exit fullscreen mode

Error Monitoring:

// Basic error logging setup
const winston = require('winston');

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' })
  ]
});

// Use in your routes
app.post('/api/something', (req, res) => {
  try {
    // Your code here
  } catch (error) {
    logger.error('API Error:', error);
    res.status(500).json({ error: 'Something went wrong' });
  }
});
Enter fullscreen mode Exit fullscreen mode

🚀 What I'd Do Differently

The "Start Over" Checklist:

  • [ ] Master one backend framework completely
  • [ ] Learn basic UI/UX principles
  • [ ] Build 3 personal projects before taking client work
  • [ ] Set clear project boundaries upfront
  • [ ] Network with designers and other developers
  • [ ] Understand basic business concepts (contracts, invoicing)
  • [ ] Always test in staging before production
  • [ ] Document everything (seriously, everything)

Quick Win Tips:

  1. Use boilerplates for common setups
  2. Test payments in sandbox first, always
  3. Set up monitoring from day one
  4. Version control everything
  5. Have a backup payment processor ready

🎯 Key Takeaways

For Beginners:

  • It's okay to not know everything
  • Clients pay you to figure things out
  • Every expert was once a beginner
  • Ask questions, lots of them

For Experienced Devs:

  • Share your knowledge with beginners
  • Remember your first client horror stories
  • Mentor when you can

For Everyone:

  • Mistakes are learning opportunities
  • Build incrementally
  • Always have a backup plan

🔗 Useful Resources


What was your biggest "learn the hard way" moment? Drop it in the comments! 👇

#beginners #webdev #nodejs #freelancing #javascript #backend #career

Top comments (11)

Collapse
 
nyah_mukassa_4b5178359b15 profile image
Nyah Mukassa

Nice article 👌. When it comes to building an authentication / authorisation system, look into using and creating a refresh token when the original token expires. This adds another layer of security also.

Collapse
 
arbythecoder profile image
Arbythecoder

Absolutely Nyah, you're spot on 👏🏽. Refresh tokens are a crucial piece of any secure auth flow, especially for long-lived sessions. I appreciate the reminder — it's one of those things beginners often overlook until they face session expiration in production 😅. Thanks for sharing this!

Collapse
 
nyah_mukassa_4b5178359b15 profile image
Nyah Mukassa

This is something I've overlooked still find difficult to implement. Where I’m confident with working with the backend, I prefer to use something like Supabase or Clerk to handle authentication. Saves me a lot of time.

Collapse
 
om_shree_0709 profile image
Om Shree

thanks

Collapse
 
arbythecoder profile image
Arbythecoder

You're very welcome, Om 🙌🏽. If you end up building your first client project soon, I hope this saves you from a few headaches. Feel free to ask questions if anything needs clarification!

Collapse
 
om_shree_0709 profile image
Om Shree

Appreciate it! 🙌🏽 I've actually been building websites for clients for the past two years, so I definitely relate to the hustle. Still, always something new to learn, thanks for sharing this, it’s super helpful! I think we should connect.

Thread Thread
 
arbythecoder profile image
Arbythecoder

Thanks for the kind words! 🙌🏽 It’s great to hear you’ve been building websites for clients too. The learning never stops in this field, right? I’d love to connect and share insights. Let’s chat more!

Thread Thread
 
om_shree_0709 profile image
Comment deleted
Thread Thread
 
arbythecoder profile image
Arbythecoder

Great, nice connecting with you.

Collapse
 
nathan_tarbert profile image
Nathan Tarbert

this is extremely impressive, so many good reminders packed in here for anyone jumping in headfirst with client work
you ever look back at one early project and realize the whole thing should've been a practice run instead of shipped to an actual client

Collapse
 
arbythecoder profile image
Arbythecoder

Haha Nathan you nailed it 😅 — I look back at my first client project like a war story. Half-baked features, missing validations, and yet I delivered it thinking I had done magic. Now I know better, and like you said, some projects really should’ve just been sandbox practice. Appreciate the kind words!

Some comments may only be visible to logged-in visitors. Sign in to view all comments.