- Knowledge
- technology
- OOP
- Tips
- Programming
- Tips
- Tutorial
- SEO
- Ranking
- Knowledge
- Special Day
- Seo
- Bug
- Data science
- Seo
- artificial intelligence
- Machine Learning
- Robotics
- happyNewYear2021
- newYearEve
- 2021
- Automation
- Smart Home
- Career
- Best Practices
- Git
- Logging
- Web Fundamentals
- DNS
- HTTPS
- Performance
- AI Tools
- ChatGPT
- Claude
- Gemini
- Laravel
- Eloquent
- MySQL
- HTTPS
- TLS
- Web Security
- Certificates
- Developer Life
- Debugging
- Docker
- DevOps
- Transactions
- Queues
- LLMs
- AI
- AI Coding
- Developer Tools
- React Native
- Expo
- Kate PMS
- Mobile Apps
- Laravel
- Authentication
- Sanctum
- Cookies
- API Design
- Payments
- Idempotency
- DeepSeek
- Open Source AI
- LLMs
- AI News
- Git
- Version Control
- AI Coding
- Prompting
- PHP
- Checklist
- MCP
- AI Agents
- OpenAI
- Architecture
- Microservices
- Modular Monolith
- Estimation
- Developer Life
- Project Planning
- Humour
- OAuth
- OpenID Connect
- Authentication
- Embeddings
- Vector Search
- RAG
- pgvector
- OpenAI
- GPT-4.1
- Codex CLI
- Events
- Testing
- Clean Code
- Maintainability
- Code Review
- Webhooks
- API
- Security
- Claude Code
- Workflow
- AI
- LLM
- Prompt Injection
- Mobile
- React
- Networking
- TCP
- UDP
- HTTP/3
- CLAUDE.md
- AWS
- Cloud Security
- Backups
- PHPUnit
- Software Engineering
- Leadership
- Communication
- RAG
- Embeddings
- AI Engineering
- IT Infrastructure
- Networking
- Access Control
- CI/CD
- GitHub Actions
- Gemini CLI
- Claude Code
- JavaScript
- Async/Await
- Node.js
- Promises
- Security
- Cryptography
- Passwords
- MySQL
- Database
- Vibe Coding
- Software Quality
- DNS
- Code Reading
- Onboarding
- Productivity
- Background Jobs
- Developer Humour
- Estimates
- Dev Life
- JWT
- o3-mini
- DeepSeek R1
- Rate Limiting
- Kate PMS
- E-Signing
- Audit Trail
- REST
- GraphQL
- API Design
- Laravel 12
- Upgrade Guide
- Open Source
- Self-Hosting
- Task Scheduling
- Cron
- Secrets
- CORS
- PHP
- PHP-FPM
- OPcache
- GitHub Copilot
- Software Architecture
- Engineering
- TypeScript
- JavaScript
- Type Safety
- AI Security
- React Native
- Product Design
- AI Agents
- Kiro
- Queues
- Redis
- RabbitMQ
- AWS SQS
- Nginx
- Apache
- GPT-5
- gpt-oss
- Clean Code
- Architecture
- Naming
- Documentation
- Career
- ADR
- Teamwork
- Supply Chain
- Kate HRM
- HR Software
- Permissions
- System Design
- Pagination
- SSH
- Linux
- Big O
- Databases
- Laravel Boost
- MCP
- Developer Skills
- Validation
- Databases
- Indexes
- Code Quality
- Deployment
- Developer Humour
- Feature Flags
- Code Review
- Pull Requests
- Docker
- Cursor
- Authorization
- RBAC
- Gemini
- Long Context
- PHP 8.4
- Caching
- Dependency Injection
- Web Performance
- Browser
- CSS
- Database
- Migrations
- ChatGPT
- AI for Developers
- Monitoring
- On-Call
- REST
- Backend
- SQL
- NoSQL
- Database Design
- Coding Agents
- Claude 4
- API Resources
- REST API
- Load Balancing
- Scaling
- AWS
- AI Tools
- Claude
- Sora 2
- CTE
- 2FA
- TOTP
- Programming Languages
- Prompts
- Developer Workflow
- API Gateway
- APIs
- Passport
- API Auth
- Learning
- Burnout
- Developer Growth
- Web Development
- SEO
- Kate Mall
- ChatGPT Atlas
- Agent Skills
- Middleware
- Laravel 12
- Collections
- Context Window
- Monitoring
- Commit Messages
- Self Review
- Growth
- Regex
- Programming Basics
- Text Processing
- Database Design
- Normalization
- Linux
- Server Security
- Linux Foundation
- Open Standards
- Legacy Code
- Documentation
- AI Workflow
- File Uploads
- Test Data
- Hashing
- Performance
- Caching
- Enums
- Scope Creep
- Estimation
- Codex
- Gemini CLI
- Timezones
- Carbon
- Bugs
- PHP 8.5
- Gemini 3
- GPT-5.1
- Data Integrity
- Event Loop
- Async
- Opus 4.5
- AI Models
- React
- Forms
- Frontend
- Backups
- AI Images
- DALL-E
- Midjourney
- Race Conditions
- Concurrency
- Legacy Code
- Refactoring
- Senior Engineer
- Scope
- LLM
- CDN
- Web
- Sub-Agents
- Soft Deletes
- Audit Log
- Concurrency
- AI Learning
- NestJS
- AI Evals
- Policies
- SPF DKIM DMARC
- Unicode
- UTF-8
- Knowledge Graph
- Value Objects
- Technical Debt
- Feature Flags
- Laravel Pennant
- Deployment
- Copilot
- Composer
- Dependencies
- Artisan
- Automation
- AWS S3
- Object Storage
- Cloud
- Small Language Models
- Ollama
- Production
- Sessions
- HTTP
- Mentoring
- SQL
- Virtual Machines
- Web Development
- HTTP/2
- QUIC
- Web Performance
- AI Integration
- LLM API
- SOLID
- OOP
- Hosting
- Serverless
- Merge Conflicts
- Temperature
- AI Development
- Reverse Proxy
- Nginx
- Infrastructure
- Verification
- Passkeys
- WebAuthn
- Teams
- Communication
- Stakeholders
- Monorepo
- CI/CD
- Versioning
- JSON Schema
- Livewire
- Inertia
- Meetings
- Distributed Systems
- Privacy
- Full-Stack
- T-Shaped Skills
- Money
- Notifications
- Web Security
- HTTP Headers
- CSP
- Function Calling
- Load Testing
- k6
- Data Extraction
- Debugging
- WebSockets
- SSE
- Real-Time
- Laravel Reverb
- Infrastructure as Code
- Terraform
- Side Projects
- Laravel Pint
- OpenAPI
- Swagger
- UX
- Multimodal
- Jest
- Pair Programming
- APIs
- Rate Limiting
- Resilience
- Dev Humour
- Design Tokens
- JWT
- API Keys
- Sessions
- PHPStan
- Rector
- Incidents
- Reporting
- Dashboards
- Zero Trust
- IAM
- Search
- Laravel Scout
- Junior Developers
- Mentoring
- Images
- WebP
- AVIF
- Bug Reports
- Let's Encrypt
- Design Docs
- Software Design
- Observers
- Replication
- Accountability
- Data Structures
- Reliability
- LLM Memory
- Error Handling
- Payments
- Payment Gateway
- Webhooks
- PCI DSS
- Observability
- OpenTelemetry
- Personal Brand
- Writing
- Conventions
- Dates
- Scheduling
- Disaster Recovery
- Compression
- Brotli
- Deadlines
- Developer Habits
- State Machines
- Tech Roles
- UUID
- ULID
- Horizon
- Planning
- Engineering Culture
- Ownership
- Soft Skills
- Socialite
- Cost Control
- Collations
- Unicode
- Octane
- PostgreSQL
Building an HR and CRM System: Lessons From Kate HRM
About Post
An HR system sounds like a boring project until you start building one. Then you realise it touches almost everyone in the company, holds some of the most sensitive data you'll ever store, and is expected to follow rules that exist mostly in people's heads and old spreadsheets.
Kate HRM is the internal system I work on at Kate that brings HR, CRM and project management into one place. It's not my first HR system either; before Kate I built a multi-tenant SaaS HRM platform as a freelancer. So this isn't a feature tour. It's the set of lessons I'd hand to anyone about to build an internal business system like this, because most of them only become obvious once you're in the middle of it.
Why one system instead of three tools
HR, CRM and project management are usually three separate products. The reason to combine them is that they keep referring to the same things: people, clients and work.
An employee is assigned to a project. The project is for a client. The client conversation is owned by an employee who might be on leave next week. When those live in three tools, every question that crosses them ("who's covering this client while she's away?") becomes a manual lookup or a spreadsheet export.
One system with shared core records makes those questions a query. That's the real value, and it's also the first lesson.
Lesson 1: get the shared core right first
The modules are the visible part, but the foundation is a handful of shared entities: employees, departments, clients, projects, and the links between them. Every module leans on them.
Get these wrong and every module inherits the problem. A few things worth deciding early:
- A person is not a user account. Not every employee logs in, and an account can be disabled while the employee record must stay for history. Keep them separate and linked.
- History is part of the data. People change department, title and manager. If you only store the current value, reports about last year quietly become wrong.
- Each module owns its own tables and talks to the core through clear relationships, not by reaching into another module's internals. That keeps the CRM from breaking when HR changes.
Lesson 2: permissions are a design problem, not a checkbox
In most apps, roles are simple: admin and user. In an HR system, the question is never just "can this person see employee records?" It's "which employees, and which fields?"
A manager may see their team's leave requests but not their personal documents. HR may see everything for employees but nothing in the sales pipeline. A project lead may see project hours but not salaries. That's three dimensions at once: the action, the scope of records, and the sensitivity of fields.
What I'd recommend:
- Use permissions, not role checks in code. Write
can('approve-leave'), neverif ($user->role === 'manager'). Roles are just bundles of permissions that the business can rearrange without a deploy. - Scope queries centrally. Put "which records may this user see" in one place, such as a query scope or policy, so a new report or export can't forget it.
- Treat sensitive fields separately. Personal and financial details deserve their own permission and should be excluded from API responses by default, not hidden in the UI.
- Test the denials. The test that matters most is "this manager can't see that department". Happy paths get tested naturally. Boundaries need deliberate tests.
The lesson I'd underline: design permissions before the second module, not after the fifth. Retrofitting record-level access onto screens and reports that assumed "everyone sees everything" is far more expensive than building it in.
Lesson 3: workflows are state machines
Leave requests, approvals, client deals and project tasks all move through states: draft, submitted, approved, rejected, cancelled. The temptation is a status column and a scattering of if statements.
The better approach is to be explicit about which transitions are allowed and who may perform them. "Approved can become cancelled, but not draft." "Only the employee's manager, or HR, can approve." Once that lives in one place, every screen and API endpoint follows the same rules, and each transition can record who did it and when. That record is what you'll need when someone asks why a request was rejected.
Lesson 4: reporting is a feature, not an afterthought
For many people, the reports are the system. Managers rarely browse records one by one. They want leave balances, headcount by department, client pipeline status and project progress, filtered by date range, and exported.
- Design the data with reports in mind. If a report needs "status on a given date", you need history, not just the current state.
- Apply the same permission scopes to reports and exports. An export is just a very efficient way to leak data if it skips them.
- Queue heavy exports and notify when ready, rather than making someone watch a spinner.
- Index for the filters people actually use, usually dates and department, and check the slow query log once real data arrives.
Lesson 5: adoption beats features
An internal system competes with the old way of doing things, and the old way is familiar. The features that drive adoption are rarely the clever ones. They're the small things: a leave request that takes seconds on a phone, sensible defaults, notifications that tell people what they need to do next, and data that's correct on the first day because someone carefully migrated the old records.
The fastest way to learn what's missing is to sit with the people who use it. No design document replaces watching someone fill in a form for the first time.
If you're about to build one
- Model the shared core (people, clients, projects) before the modules.
- Keep users and employees separate, and keep history.
- Design permissions on three axes: action, scope and sensitive fields.
- Make workflows explicit state machines with an audit trail.
- Treat reports and exports as first-class, permission-scoped features.
- Ship small, watch people use it, adjust.
Have you built an internal business system? What turned out to be harder than it looked: permissions, reporting, or getting people to actually use it?

Be first to comment it...