- 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
Notifications Done Right in a Property Management App
About Post
Every notification is a tiny tap on someone's shoulder. One at the right moment is helpful. Five in a row, about things they don't care about, and they do the only sensible thing: they turn notifications off.
And then they miss the one that mattered. The contract waiting for their signature. The water shutdown tomorrow morning. The rent reminder that arrived while the notifications were switched off.
That's the real cost of bad notifications, and it's why I treat them as a product feature in Kate PMS, not as a line of code at the end of a controller. Kate PMS has a web portal for staff, a staff app and a tenant app, and a lot of things happen across them: contracts and e-signing, rent and payments, maintenance requests. Almost every one of those is something somebody might want to hear about. The hard part is deciding who, how, and when.
Here are the questions I ask for every notification, in the order I ask them.
1. Does anyone actually need to know?
The easiest notification to get right is the one you don't send. When a maintenance request moves from "assigned" to "in progress" to "parts ordered" to "technician scheduled", the tenant probably cares about two of those steps, not all four. Internal status changes are often for staff, not for the tenant.
A good test: would this person do something differently because of this message? If the answer is no, it belongs in the app's activity history, where they can find it when they want it, not in their pocket.
2. How urgent is it? That decides the channel
Each channel has a personality, and using the wrong one is how notifications become noise.
| Channel | Best for | Avoid for |
|---|---|---|
| In-app (inbox) | History, low-urgency updates, anything people check on their own time | Anything with a deadline |
| Push | Timely, actionable things: "your request was scheduled", "a contract is ready to sign" | Marketing, long content, things that need a record |
| Documents and records: receipts, signed contracts, statements | Anything that needs attention in the next hour | |
| SMS | Rare, important and time-sensitive messages | Almost everything else |
Often the right answer is two channels with different jobs: a push to say "your payment receipt is ready" and an email that holds the receipt itself. The push is the tap on the shoulder; the email is the paperwork.
3. Can the person control it?
Preferences sound like a settings-screen chore. They're actually what makes people trust the notifications they keep. If the only choice is "everything or nothing", many people choose nothing.
The model I like is preferences per category, not per message: payments, maintenance updates, contract and documents, building announcements. A handful of switches people understand, rather than forty they never touch.
Some messages shouldn't be switchable at all. Legal and contractual messages, security alerts and payment confirmations are things the system must deliver. The honest way to handle that is to say so on the settings screen, not to hide it.
4. Will it arrive once, at a sensible time?
This is where the engineering lives. A few rules I don't bend:
- Always queued. Sending an email or a push inside a web request makes the request slow and makes it fail when the provider has a bad minute. A queue absorbs both.
- Only after the data is committed. If a notification is dispatched inside a database transaction that later rolls back, you've told someone about a contract that doesn't exist. Laravel's queued notifications can be told to wait until the transaction commits.
- Checked again at send time. A rent reminder queued for 9 a.m. should check that the rent is still unpaid at 9 a.m. Nothing destroys trust faster than "your rent is due" arriving right after the receipt.
- Safe to retry. Queues deliver at least once, so a crash at the wrong moment can mean a retry. Duplicate reminders are annoying; duplicate SMS cost money. Notifications need the same idempotency thinking as payments.
- Batched where it makes sense. Ten updates in five minutes should become one message, or a daily summary for staff who want one.
In Laravel, the "checked again" rule fits naturally into a notification's shouldSend method, which runs right before each channel sends (simplified):
class RentDueReminder extends Notification implements ShouldQueue
{
use Queueable;
public function __construct(public Invoice $invoice) {}
public function via(object $notifiable): array
{
return $notifiable->channelsFor('payments'); // from the user's preferences
}
public function shouldSend(object $notifiable, string $channel): bool
{
return $this->invoice->fresh()->isUnpaid();
}
}
Notice what via() does: the notification doesn't decide the channels, the user's preferences do. The notification describes the event; the person decides how they hear about it.
The rule: a notification should be true when it arrives, not just when it was queued. Re-check the state at send time, every time.
5. Does it read well on a lock screen?
A push notification has about one line to do its job. Templates help keep messages consistent, but the content still needs care:
- Lead with the action or the fact: "Contract ready to sign" beats "Update regarding your tenancy".
- Deep link to the exact screen. A push that opens the app's home screen and leaves the person to hunt for the contract is half a notification.
- Keep sensitive details off the lock screen. "A new payment receipt is available" is fine. Amounts and personal details belong inside the app, behind login.
- Write templates as copy, not code. Keep them in one place, reviewed like any other text users see, and in every language you support.
6. Can we prove it was sent?
In property management, "I never got that notice" is a real conversation. Keeping a record of what was sent, to whom, on which channel and when turns that from an argument into a lookup. It also helps support staff answer "did the tenant get the reminder?" without asking a developer to dig through logs.
The short version
- Send fewer notifications. Each one should lead to an action or answer a real question.
- Pick the channel by urgency: in-app for history, push for timely, email for records, SMS for rare and important.
- Give people category-level preferences, and be honest about what they can't switch off.
- Queue everything, send after commit, re-check at send time, and make retries safe.
- Write for the lock screen and deep link to the right place.
- Keep a record of what was sent.
Which app has the best notifications on your phone right now, and which one did you mute first?

Be first to comment it...