Intro
Feed
A collection of short notes, interesting links, and the occasional long form post.
Items
-
- Published
(based on writing https://alexkrupp.typepad.com/sensemaking/2021/06/django-for-startup-founders-a-better-software-architecture-for-saas-startups-and-consumer-apps.html)
You are an expert in Python, Django, and scalable web application development.
Predictability
-
Every Endpoint Should Tell a Story
-
Explanation: Structure each REST API endpoint to follow a consistent pattern. This makes your code predictable and easier to understand. The pattern includes specifying permissions, handling inputs, performing business logic, and returning responses in a standard way.
-
Example:
from rest_framework.views import APIView from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response class UserProfileView(APIView): permission_classes = [IsAuthenticated] def get(self, request): # Step 1: Copy input to local variables user_id = request.query_params.get('user_id') # Step 2: Sanitize input user_id = sanitize_input(user_id) # Step 3: Validate input if not user_id: return Response({'error': 'User ID is required.'}, status=400) # Step 4: Enforce business requirements if not user_exists(user_id): return Response({'error': 'User does not exist.'}, status=404) # Step 5: Perform business logic user_profile = get_user_profile(user_id) # Step 6: Return HTTP response return Response({'data': user_profile}, status=200)
-
-
Keep Business Logic in Services
-
Explanation: Separate your business logic from views and models. Place it in service modules to promote reusability and maintainability.
-
Example:
# services/user_services.py def get_user_profile(user_id): # Complex logic to retrieve and process user profile pass # views.py from services.user_services import get_user_profile
-
-
Make Services the Locus of Reusability
-
Explanation: Reuse service methods across your project wherever the same functionality is needed. This avoids code duplication and keeps your logic consistent.
-
Example:
# services/notification_services.py def send_welcome_email(user_email): # Logic to send email pass # Used in multiple places send_welcome_email(user_email)
-
-
Always Sanitize User Input, Sometimes Save Raw Input, Always Escape Output
-
Explanation: Sanitize all user inputs immediately to prevent security vulnerabilities like XSS attacks. Save raw input only when necessary for future reference, and always escape outputs when displaying data.
-
Example:
import html def sanitize_input(input_value): return html.escape(input_value) # Sanitize input user_input = sanitize_input(request.data.get('comment'))
-
-
Don’t Split Files by Default & Never Split Your URLs File
-
Explanation: Keep related code together to make it easier to find and understand. Avoid unnecessary splitting of files, especially the
urls.pyfile. -
Example:
- Keep all URL patterns in a single
urls.pyfile with clear comments separating sections.
# urls.py from django.urls import path from .views import UserProfileView, UserSettingsView urlpatterns = [ # User-related URLs path('user/profile/', UserProfileView.as_view(), name='user-profile'), path('user/settings/', UserSettingsView.as_view(), name='user-settings'), # Other sections... ] - Keep all URL patterns in a single
-
Readability
-
Each Variable’s Type or Kind Should Be Obvious from Its Name
-
Explanation: Use clear and descriptive names for variables that indicate their type or purpose, reducing confusion.
-
Example:
user_list = [] user_dict = {} is_active_user = True
-
-
Assign Unique Names to Files, Classes, and Functions
-
Explanation: Ensure that each file, class, and function has a unique name to avoid conflicts and improve searchability.
-
Example:
- Instead of multiple
views.pyfiles, useuser_views.py,product_views.py.
- Instead of multiple
-
-
**Avoid
*argsand**kwargsin User Code**-
Explanation: Use explicit parameters in functions to enhance clarity and prevent unexpected behaviors.
-
Example:
def create_user(username, email): pass # Good def create_user(*args, **kwargs): pass # Avoid this
-
-
Use Functions, Not Classes
-
Explanation:
- Simplicity and Clarity: Functions are simpler and more straightforward than classes. They perform specific tasks and are easier to read, understand, and test.
- Avoid Unnecessary Complexity: Classes introduce complexity with state management, inheritance, and side effects, which can make the code harder to maintain, especially for small to medium-sized projects.
- Functional Programming Benefits: Emphasizing functions aligns with functional programming principles, leading to more predictable and bug-resistant code.
- Statelessness: Functions that avoid maintaining state reduce the likelihood of unintended interactions and make concurrent execution safer.
-
Guidelines:
- Use plain functions for operations that don’t require object-oriented features.
- Only use classes when you need to maintain state across multiple function calls or when leveraging polymorphism and inheritance is essential.
- Favor pure functions that return outputs solely based on inputs without side effects.
-
Examples:
# Good Practice: Using Functions # utils/math_utils.py def calculate_discount(price, discount_percent): return price * (discount_percent / 100) def apply_tax(price, tax_rate): return price + (price * (tax_rate / 100)) # Usage in your code discounted_price = calculate_discount(original_price, 10) final_price = apply_tax(discounted_price, 5)# Avoid: Unnecessary Use of Classes # utils/math_utils.py class PriceCalculator: def __init__(self, price): self.price = price def calculate_discount(self, discount_percent): return self.price * (discount_percent / 100) def apply_tax(self, tax_rate): return self.price + (self.price * (tax_rate / 100)) # Usage in your code calculator = PriceCalculator(original_price) discounted_price = calculator.calculate_discount(10) final_price = calculator.apply_tax(5)Why Prefer Functions Over Classes in This Context:
- Reduced Boilerplate: Functions eliminate the need for boilerplate code like
__init__methods. - Easier Testing: Pure functions are easier to test since they don’t rely on or alter external state.
- Better Readability: Functions focus on performing a single task, making the code more readable and maintainable.
- Avoiding Side Effects: Without class state, there’s a lower risk of side effects from shared mutable data.
- Reduced Boilerplate: Functions eliminate the need for boilerplate code like
-
When to Use Classes:
- Stateful Operations: If you need to maintain state between function calls.
- Complex Data Structures: When modeling entities with both data and behaviors tightly coupled together.
- Inheritance and Polymorphism: When you need to extend or modify behaviors through inheritance hierarchies.
-
Example of Appropriate Class Use:
# models/user.py class User: def __init__(self, username, email): self.username = username self.email = email def send_email(self, subject, message): # Logic to send email to self.email pass # Using the User class user = User('john_doe', '[email protected]') user.send_email('Welcome', 'Thank you for signing up!')In this case, using a class makes sense because
Userrepresents an entity with attributes and behaviors.
-
-
There Are Exactly Four Types of Errors
-
Explanation:
Standardizing error handling across your application improves consistency and makes your code easier to maintain and debug. The four types of errors are:
- Upstream Errors:
- Errors that originate from middleware or external services before reaching your application logic.
- Handling: Generally, allow these errors to propagate. Customize handling only if necessary.
- Validation Errors:
- Occur when user input fails to meet predefined validation rules (e.g., missing fields, invalid formats).
- Handling: Collect and return all validation errors together with descriptive messages.
- Business Requirement Errors:
- Occur when input is valid, but the action violates business rules (e.g., insufficient funds, access denied).
- Handling: Return specific error messages indicating why the action cannot be completed.
- Internal Errors (500 Errors):
- Unexpected errors due to bugs or exceptions in the server.
- Handling: Log details for debugging but return a generic error message to the client to avoid exposing sensitive information.
- Upstream Errors:
-
Guidelines:
- Use consistent error response structures.
- Include meaningful error messages and appropriate HTTP status codes.
- Avoid exposing internal implementation details in error messages sent to clients.
- Implement centralized error handling where possible to maintain consistency.
-
Examples:
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status class TransactionView(APIView): permission_classes = [IsAuthenticated] def post(self, request): # 1. Copy and sanitize inputs amount = sanitize_input(request.data.get('amount')) recipient_id = sanitize_input(request.data.get('recipient_id')) # 2. Validate inputs errors = {} if not amount: errors['amount'] = ['Amount is required.'] elif not is_valid_amount(amount): errors['amount'] = ['Invalid amount format.'] if not recipient_id: errors['recipient_id'] = ['Recipient ID is required.'] if errors: # **Validation Error** return Response({'errors': errors}, status=status.HTTP_400_BAD_REQUEST) # 3. Business logic and requirements if not has_sufficient_funds(request.user, amount): # **Business Requirement Error** return Response( {'error': 'Insufficient funds.'}, status=status.HTTP_403_FORBIDDEN ) if not recipient_exists(recipient_id): return Response( {'error': 'Recipient not found.'}, status=status.HTTP_404_NOT_FOUND ) try: # 4. Perform transaction transaction = perform_transaction(request.user, recipient_id, amount) except ExternalServiceError as e: # **Upstream Error** return Response( {'error': 'Transaction service is unavailable.'}, status=status.HTTP_503_SERVICE_UNAVAILABLE ) except Exception as e: # **Internal Error** log_error(e) return Response( {'error': 'An unexpected error occurred.'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR ) # 5. Success response return Response({'message': 'Transaction completed successfully.'}, status=status.HTTP_200_OK) -
Detailed Breakdown:
-
Validation Errors (400 Bad Request):
- Collect all input validation errors.
- Return them together so the client can fix all issues at once.
if errors: return Response({'errors': errors}, status=status.HTTP_400_BAD_REQUEST)Error Response Structure:
{ "errors": { "amount": ["Amount is required."], "recipient_id": ["Recipient ID is required."] } } -
Business Requirement Errors (403 Forbidden, 404 Not Found):
- Return a specific error message indicating the business rule violation.
- Use appropriate status codes to reflect the error type.
if not has_sufficient_funds(request.user, amount): return Response( {'error': 'Insufficient funds.'}, status=status.HTTP_403_FORBIDDEN )Error Response Structure:
{ "error": "Insufficient funds." } -
Upstream Errors (503 Service Unavailable):
- Errors from external services or middleware.
- Communicate service unavailability without exposing internal details.
except ExternalServiceError as e: return Response( {'error': 'Transaction service is unavailable.'}, status=status.HTTP_503_SERVICE_UNAVAILABLE ) -
Internal Errors (500 Internal Server Error):
- Catch-all for unexpected exceptions.
- Log the exception details internally.
- Return a generic error message to the client.
except Exception as e: log_error(e) return Response( {'error': 'An unexpected error occurred.'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR )
-
-
Consistent Error Response Structure:
-
For Validation Errors:
{ "errors": { "field_name": ["Error message."] } } -
For Business Requirement, Upstream, and Internal Errors:
{ "error": "Error message." }
-
-
Best Practices:
- Logging:
- Always log exceptions and errors on the server side for auditing and debugging purposes.
- User-Friendly Messages:
- Provide clear and actionable error messages without exposing sensitive information.
- HTTP Status Codes:
- Use appropriate status codes to represent the error type (e.g., 400, 403, 404, 500).
- Avoid Exception Swallowing:
- Do not catch exceptions without handling them properly. This could mask issues and make debugging difficult.
- Error Handling Middleware:
- Implement middleware to handle uncaught exceptions globally, ensuring consistent error responses.
- Logging:
-
Simplicity
-
URL Parameters Are a Scam
-
Explanation: Avoid using URL parameters. Use query parameters for GET requests and body parameters for POST/PUT requests for consistency.
-
Example:
# Instead of this path('user/<int:user_id>/', UserProfileView.as_view(), name='user-profile') # Use this path('user/profile/', UserProfileView.as_view(), name='user-profile') # And pass user_id as a query parameter: /user/profile/?user_id=123
-
-
Write Tests. Not Too Many. Mostly Integration.
-
Explanation: Focus on writing integration tests for your endpoints to ensure they work correctly when combined. Don’t overdo unit tests.
-
Example:
from rest_framework.test import APITestCase class UserProfileTests(APITestCase): def test_get_user_profile(self): response = self.client.get('/user/profile/', {'user_id': 1}) self.assertEqual(response.status_code, 200)
-
-
Treat Unit Tests as a Specialist Tool
-
Explanation: Use unit tests sparingly for complex functions or algorithms that need isolated testing.
-
Example:
import unittest def calculate_discount(price, percentage): return price * (percentage / 100) class DiscountTests(unittest.TestCase): def test_calculate_discount(self): self.assertEqual(calculate_discount(100, 10), 10)
-
-
Use Serializers Responsibly, or Not at All
-
Explanation: Use serializers for input validation and output formatting, but avoid overcomplicating them. Consider simpler alternatives when appropriate.
-
Example:
from rest_framework import serializers class UserSerializer(serializers.Serializer): username = serializers.CharField(max_length=100) email = serializers.EmailField()
-
-
Write Admin Functionality as API Endpoints
-
Explanation: Implement admin actions as API endpoints with proper permissions. This keeps logic consistent and testable.
-
Example:
# views.py class AdminUserListView(APIView): permission_classes = [IsAdminUser] def get(self, request): users = get_all_users() return Response({'users': users}, status=200)
-
Upgradability
-
Your App Lives Until Your Dependencies Die
-
Explanation: Choose well-maintained and widely-used dependencies. Keep them updated to ensure long-term sustainability.
-
Example:
- Use the latest stable versions of Django and other libraries.
- Regularly update dependencies using
pip-upgrade.
-
-
Keep Logic Out of the Front End
-
Explanation: Minimize business logic in the front end. Handle it on the server side to simplify maintenance and upgrades.
-
Example:
- Perform data processing in views or services, and send prepared data to the front end.
-
-
Don’t Break Core Dependencies
-
Explanation: Avoid altering or making assumptions about the internal behavior of core frameworks and libraries. Use them as intended.
-
Example:
- Don’t override internal Django methods unless absolutely necessary.
- Avoid hacking into library internals to change behavior.
-
Using uv for the project, and to run manage.py use uv run python manage.py etc
-
The Curse of Kafka by Andrea Bajani
archive.is
-
The Best Tacit Knowledge Videos on Every Subject
www.lesswrong.com
-
- Published
…if anything I want to become what I’ve called an information trillionaire, I’m not going to make that but its a good aspiration to have, just collect more information and be an information trillionaire…
-
Tennis Player Actions Dataset for Human Pose Estimation
data.mendeley.com
-
- Published
canScroll() { return false } wasn’t working on my custom shape. Did this instead.
const TableComponent = () => { const containerRef = useRef<HTMLDivElement>(null); useEffect(() => { const containerElement = containerRef.current; const handleWheel = (event: WheelEvent) => { const isOverCardShape = containerElement?.contains(event.target as Node); // to determine if the event should be prevented if (isOverCardShape) { // event.preventDefault(); event.stopPropagation(); } }; containerElement?.addEventListener("wheel", handleWheel, { passive: false, }); return () => { containerElement?.removeEventListener("wheel", handleWheel); }; }, [containerRef]); ... -
An Autumn Afternoon. A 1962 Japanese drama film directed by Yasujirō Ozu.
en.wikipedia.org
-
- Published
import { DayPicker } from "react-day-picker"; import "react-day-picker/style.css"; function MyDatePicker() { const [selected, setSelected] = useState<Date>(); return ( <DayPicker mode="single" selected={selected} onSelect={setSelected} footer={ selected ? `Selected: ${selected.toLocaleDateString()}` : "Pick a day." } /> ); }DayPicker + Radix UI Popover
"use client"; import React from "react"; import { useState } from "react"; import { DayPicker } from "react-day-picker"; import * as Popover from "@radix-ui/react-popover"; import { format } from "date-fns"; import "react-day-picker/style.css"; import { useRouter } from "next/navigation"; export function MyDatePicker({ displayDate }: { displayDate: string }) { const router = useRouter(); const [selected, setSelected] = useState<Date>(); const [open, setOpen] = useState(false); const handleDateSelect = (date: Date) => { setSelected(date); const formattedDate = format(date, "yyyy-MM-dd"); router.push(`/schedule?date=${formattedDate}`); setOpen(false); }; return ( <Popover.Root open={open} onOpenChange={setOpen}> <Popover.Trigger asChild> <button className="display-date">{displayDate}</button> </Popover.Trigger> <Popover.Content className="PopoverContent"> <DayPicker mode="single" selected={selected} onDayClick={handleDateSelect} footer={ selected ? `Selected: ${selected.toLocaleDateString()}` : "Pick a day." } /> </Popover.Content> </Popover.Root> ); } -
Modern date range picker component for React using Tailwind 3 and dayjs.
github.com
-
Part II: Apocalypse Now? Peter Thiel on Ancient Prophecies and Modern Tech
www.youtube.com
-
The Curse of Kafka
archive.is
-
3D force-directed graph component using ThreeJS/WebGL
vasturiano.github.io
-
Travels
www.michaelcrichton.com
-
Reading Notes on “Lost Time: Lectures on Proust in a Soviet Prison Camp”
nabeelqu.substack.com
-
- Published
Astro + Temporal try
This code demonstrates how to initiate a workflow in Astro using the Temporal client
-
Tools for Thought Rocks: October 2022 - Pol Baladas, Paul Shen
www.youtube.com
-
Adaptive fictions
www.thebeliever.net
-
Calendar Widget Interaction
ui.lndev.me
-
A markdown-like journal language for plainly writing logs
timeline.markwhen.com
-
Authenticating users in Astro with Better Auth: A Step-by-Step Guide
www.launchfa.st
Looks easy and promising
--- if (!Astro.locals.user?.id) return Astro.redirect('/signin') --- <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> </head> <body> {JSON.stringify(Astro.locals.user)} <button id="signOutButton">Sign Out</button> <script> import { authClient } from '@/auth-client' document.getElementById('signOutButton')?.addEventListener('click', async () => { await authClient.signOut() window.location.href = '/signin' }) </script> </body> </html> -
Adaptive dotted background pattern
codepen.io
html { background-image: radial-gradient(circle at center, transparent, Canvas), radial-gradient( circle at center, color-mix(in oklch, Canvastext 33%, Canvas) 0.8px, transparent 1.3px ); background-size: 200px 200px, 40px 40px; background-repeat: round, space; } -
A unified theory of fucks
aworkinglibrary.com
Good work is the art of giving a fuck about the living.