TypeScript

Our codebase follows the Vercel Engineering Style Guide conventions.

ESLint Configuration

We maintain three primary ESLint configurations for different project types:
  1. Library Configuration (for packages):
extends: [
"@vercel/style-guide/eslint/node",
"@vercel/style-guide/eslint/typescript"
]
  1. React Configuration (for React applications):
extends: [
"@vercel/style-guide/eslint/node",
"@vercel/style-guide/eslint/typescript",
"@vercel/style-guide/eslint/browser",
"@vercel/style-guide/eslint/react",
"@vercel/style-guide/eslint/next"
]
  1. Next.js Configuration (for Next.js applications):
extends: [
"@vercel/style-guide/eslint/node",
"@vercel/style-guide/eslint/typescript",
"@vercel/style-guide/eslint/browser",
"@vercel/style-guide/eslint/react",
"@vercel/style-guide/eslint/next"
]

Key Conventions

  1. TypeScript Usage
    • Strict TypeScript checking enabled
    • Explicit type annotations when necessary
    • Proper interface and type naming (prefix with T for types, I for interfaces when helpful)
    • No use of any type unless absolutely necessary
  2. Imports/Exports
    • Follow strict import ordering:
      1. Mocks (for testing)
      2. Server-only imports
      3. Third-party modules
      4. Internal @formbricks/* modules
      5. Local aliases (~/*)
      6. Relative imports
  3. Error Handling
    • Use typed error responses
    • Proper error propagation
    • Consistent error message formatting
    • Implement error boundaries in React components
  4. Async/Await
    • Prefer async/await over raw promises
    • Proper error handling in async functions
    • Use Promise.all for parallel operations
  5. React Specific
    • Functional components with TypeScript
    • Proper use of hooks
    • Consistent prop typing
    • Server Components by default in Next.js App Router

Code Formatting

We use Prettier with specific configurations:
{
bracketSpacing: true,
bracketSameLine: true,
singleQuote: false,
jsxSingleQuote: false,
trailingComma: "es5",
semi: true,
printWidth: 110,
arrowParens: "always"
}

Swift (iOS SDK)

Our iOS SDK follows Swift best practices.

Swift Configuration

The iOS SDK requires the following:
  • Swift Version: 5.7+
  • Platform: iOS 16.6+
  • Package Manager: Swift Package Manager and CocoaPods support
  • ARC: Automatic Reference Counting enabled
Package.swift Configuration:
// swift-tools-version:5.7
platforms: [
  .iOS(.v16)
],
CocoaPods Configuration:
s.platform = :ios, "16.6"
s.swift_version = "5.7"
s.requires_arc = true

Key Conventions

  1. Access Control Strategy
    • public: SDK public API surface only
    • internal: Internal SDK communication and shared components
    • private: Implementation details within specific classes
    • Strategic use of private(set) for read-only public properties
  2. Architecture Patterns
    • Singleton Pattern: Main SDK class (Formbricks) with static interface
    • Manager Pattern: Specialized managers (UserManager, SurveyManager, PresentSurveyManager)
    • Builder Pattern: Configuration objects (FormbricksConfig.Builder)
    • Protocol-Oriented Programming: Service protocols for dependency injection and testing
  3. Error Handling
    • Custom error enums with descriptive cases (FormbricksSDKErrorType)
    • Error types conform to LocalizedError protocol
    • Structured error propagation with completion handlers
    • Defensive programming with guard statements and early returns
  4. Naming Conventions
    • Classes: PascalCase (FormbricksConfig, UserManager)
    • Properties/Methods: camelCase (environmentId, setUserId)
    • Constants: camelCase with descriptive names
    • Protocol names: Descriptive with “Protocol” suffix (FormbricksServiceProtocol)
  5. Code Organization
    • // MARK: comments for logical section separation
    • Extensions for related functionality grouping
    • Consistent file structure with models, managers, networking, and views
  6. Model Design
    • Prefer struct for data models and value types
    • Use class for reference types and managers
    • Implement Codable for JSON serialization/deserialization
    • Immutable properties where possible (let over var)
  7. Security & Validation
    • HTTPS enforcement for all network requests
    • URL validation before network operations
    • Input validation with descriptive error messages
    • Secure data handling practices
  8. Asynchronous Operations
    • OperationQueue for network operations
    • Completion handlers for async operations
    • Network connectivity checking with NWPathMonitor
    • Thread-safe operations with proper queue management

Code Formatting

We follow standard Swift formatting conventions: Key Formatting Rules:
// Class definitions
@objc(Formbricks) public class Formbricks: NSObject {

// Property declarations with access control
static internal var isInitialized: Bool = false
private let userManager: UserManager

// Method signatures with clear parameter labels
@objc public static func setup(with config: FormbricksConfig, force: Bool = false)

// Guard statements for early returns
guard !isInitialized else {
    let error = FormbricksSDKError(type: .sdkIsAlreadyInitialized)
    Formbricks.logger?.error(error.message)
    return
}

// Enum cases with descriptive names
public enum FormbricksSDKErrorType: Int {
    case sdkIsNotInitialized
    case invalidAppUrl
    case networkError
}

Kotlin (Android SDK)

Our Android SDK codebase with Kotlin follows modern Android development practices and Kotlin conventions.

Key Conventions

  1. Package Structure
    • Logical grouping by functionality (api, model, network, manager, webview)
    • Clear separation of concerns across packages
  2. Kotlin Language Features
    • Object singletons for stateless utilities and managers (Formbricks, Logger, SDKError)
    • Data classes for models with automatic equals/hashCode/toString (Survey, User)
    • Sealed classes for representing restricted hierarchies
    • Extension functions for utility methods (Guard.kt, DateExtensions.kt)
    • Coroutines for asynchronous operations with proper context switching
  3. Error Handling
    • Centralized error definitions in SDKError object
    • Use of Result<T> type for API responses
    • Proper exception propagation with meaningful error messages
    • Consistent error logging through centralized Logger
  4. Android-Specific Patterns
    • @Keep annotations for ProGuard/R8 compatibility on public APIs
    • Proper lifecycle management in fragments and view models
    • Use of FragmentManager for UI components
    • Network security configuration for HTTPS enforcement
  5. Async/Await Pattern
    • Prefer coroutines with suspend functions over callbacks
    • Use withContext(Dispatchers.IO) for network operations
    • Implement retry logic with delay() for robust API calls
    • Proper error handling in async functions
  6. API and Network Layer
    • Retrofit for HTTP client with Gson converter
    • OkHttp interceptors for logging and security
    • Proper timeout configurations
    • Result-based API responses with retry mechanisms

Code Formatting

We use Android Studio’s default Kotlin formatting

Data Modeling

  1. Serialization
    • Kotlinx Serialization for modern JSON handling
    • Gson annotations for backward compatibility: @SerializedName
    • Consistent nullable and non-nullable field declarations
  2. Data Classes
    • Immutable data structures where possible
    • Proper use of nullable types (String?)
    • Clear property naming and documentation