Skip to content

Migration Guide: Version 1.0.0

This guide helps you migrate from PromptManager v0.9.x to v1.0.0, which represents the first stable release with a commitment to semantic versioning.

Overview

Version 1.0.0 focuses on API stabilization, performance improvements, and enhanced developer experience. While there are some breaking changes, most applications should migrate smoothly.

Major Changes

1. Stable Public API

  • All public APIs are now considered stable
  • Semantic versioning is strictly followed from this version forward
  • Deprecation warnings will be provided for 6 months before removal

2. Enhanced Storage System

  • New storage adapter capabilities
  • Improved error handling and diagnostics
  • Better performance for large prompt libraries

3. Advanced Template Features

  • Template inheritance system
  • Custom directive registration
  • Improved ERB integration

Breaking Changes

1. Removed Deprecated Methods

Methods Removed in v1.0.0

# These methods were deprecated in v0.9.x and removed in v1.0.0

# OLD (removed):
prompt.enable_erb = true
prompt.enable_envar = true

# NEW (use during initialization):
prompt = PromptManager::Prompt.new(
  id: 'template',
  erb_flag: true,
  envar_flag: true
)

Migration Steps

  1. Remove usage of deprecated setter methods
  2. Pass flags during prompt initialization

2. Storage Adapter Method Signatures

Updated Method Signatures

# OLD (v0.9.x):
def read(prompt_id)
  # Simple string return
end

# NEW (v1.0.0):
def read(prompt_id)
  # Can return string or PromptData object
end

# NEW: Enhanced read method (optional)
def read_with_metadata(prompt_id)
  {
    content: "prompt content",
    metadata: { author: "user", version: 1 },
    last_modified: Time.current
  }
end

Migration Steps

  • No changes required for basic adapters
  • Optionally implement read_with_metadata for enhanced functionality

3. Configuration Changes

Updated Configuration Structure

# OLD (v0.9.x):
PromptManager.configure do |config|
  config.cache_prompts = true
  config.cache_ttl = 300
end

# NEW (v1.0.0):
PromptManager.configure do |config|
  config.cache = {
    enabled: true,
    ttl: 300,
    store: :memory,  # or :redis, :memcached
    options: {}
  }
end

Migration Steps

  1. Update cache configuration to use nested structure
  2. Specify cache store type explicitly

New Features

1. Template Inheritance

Create reusable template hierarchies:

# layouts/base.txt
<!DOCTYPE html>
<html>
<head>
  <title>//yield title</title>
</head>
<body>
  <h1>//yield heading</h1>
  <div class="content">
    //yield content
  </div>
</body>
</html>

# email.txt
//extends layouts/base
//section title: Email Notification
//section heading: Important Update
//section content: Your account has been updated.

2. Custom Directive System

Register custom processing directives:

PromptManager.configure do |config|
  config.register_directive('timestamp') do |format, context|
    Time.current.strftime(format || '%Y-%m-%d %H:%M:%S')
  end

  config.register_directive('user_info') do |user_id, context|
    user = User.find(user_id)
    "#{user.name} (#{user.email})"
  end
end

# Usage in prompts:
# Generated at: //timestamp %B %d, %Y
# User: //user_info [USER_ID]

3. Enhanced Error Information

More detailed error messages and context:

begin
  prompt.render(incomplete_params)
rescue PromptManager::MissingParametersError => e
  puts "Missing parameters:"
  e.missing_parameters.each do |param|
    puts "  - #{param[:name]} (line #{param[:line]})"
  end

  puts "Suggestions:"
  e.suggestions.each { |suggestion| puts "  - #{suggestion}" }
end

4. Performance Monitoring

Built-in performance tracking:

PromptManager.configure do |config|
  config.performance_monitoring = {
    enabled: true,
    slow_threshold: 0.1,  # seconds
    callback: ->(metrics) {
      Rails.logger.warn "Slow prompt: #{metrics[:prompt_id]} (#{metrics[:duration]}s)"
    }
  }
end

Database Schema Updates (ActiveRecord Adapter)

New Schema for Enhanced Features

class UpdatePromptsForV100 < ActiveRecord::Migration[7.0]
  def change
    # Add versioning support
    add_column :prompts, :version_number, :integer, default: 1
    add_column :prompts, :parent_version, :integer

    # Add template inheritance
    add_column :prompts, :extends, :string
    add_column :prompts, :template_type, :string, default: 'prompt'

    # Add usage tracking
    add_column :prompts, :usage_count, :integer, default: 0
    add_column :prompts, :last_used_at, :timestamp

    # Add performance tracking
    add_column :prompts, :avg_render_time, :decimal, precision: 8, scale: 4
    add_column :prompts, :last_render_time, :decimal, precision: 8, scale: 4

    # Enhanced metadata
    change_column :prompts, :metadata, :jsonb  # PostgreSQL
    # For MySQL: change_column :prompts, :metadata, :json

    # New indexes
    add_index :prompts, :version_number
    add_index :prompts, :template_type
    add_index :prompts, :usage_count
    add_index :prompts, :last_used_at
    add_index :prompts, [:extends, :template_type]

    # GIN index for metadata search (PostgreSQL)
    add_index :prompts, :metadata, using: :gin
  end

  def down
    remove_column :prompts, :version_number
    remove_column :prompts, :parent_version
    remove_column :prompts, :extends
    remove_column :prompts, :template_type
    remove_column :prompts, :usage_count
    remove_column :prompts, :last_used_at
    remove_column :prompts, :avg_render_time
    remove_column :prompts, :last_render_time
  end
end

Configuration Migration

Full Configuration Example

# config/initializers/prompt_manager.rb (v1.0.0)

PromptManager.configure do |config|
  # Storage configuration
  config.storage = if Rails.env.production?
    PromptManager::Storage::ActiveRecordAdapter.new(
      model_class: Prompt,
      enable_versioning: true,
      enable_usage_tracking: true
    )
  else
    PromptManager::Storage::FileSystemAdapter.new(
      prompts_dir: Rails.root.join('app', 'prompts'),
      enable_hot_reload: Rails.env.development?
    )
  end

  # Caching configuration
  config.cache = {
    enabled: Rails.env.production?,
    ttl: 1.hour,
    store: :redis,
    options: {
      url: ENV['REDIS_URL'],
      namespace: 'prompt_manager',
      compress: true
    }
  }

  # Template processing
  config.templates = {
    inheritance_enabled: true,
    max_inheritance_depth: 5,
    custom_directives: true
  }

  # Performance monitoring
  config.performance_monitoring = {
    enabled: Rails.env.production?,
    slow_threshold: 0.5,
    callback: ->(metrics) {
      Rails.logger.info "PromptManager: #{metrics[:prompt_id]} rendered in #{metrics[:duration]}s"

      if metrics[:duration] > config.performance_monitoring[:slow_threshold]
        SlackNotifier.notify("Slow prompt detected: #{metrics[:prompt_id]}")
      end
    }
  }

  # Error handling
  config.error_handling = {
    raise_on_missing_prompts: Rails.env.development?,
    raise_on_missing_parameters: Rails.env.development?,
    fallback_handler: ->(error, context) {
      Rails.logger.error "PromptManager error: #{error.message}"
      Rails.logger.error "Context: #{context.inspect}"

      case error
      when PromptManager::PromptNotFoundError
        "[Prompt temporarily unavailable]"
      when PromptManager::MissingParametersError
        "[Missing: #{error.missing_parameters.map(&:name).join(', ')}]"
      else
        "[Content unavailable]"
      end
    }
  }

  # Custom directives
  config.register_directive('format_currency') do |amount, currency, context|
    # Custom currency formatting logic
    Money.new(amount.to_f * 100, currency).format
  end

  config.register_directive('user_avatar') do |user_id, size, context|
    user = User.find(user_id)
    user.avatar.variant(resize: "#{size}x#{size}").url
  end
end

Step-by-Step Migration

1. Update Dependencies

# Gemfile
gem 'prompt_manager', '~> 1.0.0'

# If using additional features:
gem 'redis-rails'           # For Redis caching
gem 'money-rails'          # For currency formatting example

2. Run Database Migration

rails generate migration UpdatePromptsForV100
# Edit the migration with schema changes above
rails db:migrate

3. Update Configuration

Replace your existing configuration with the new structure shown above.

4. Update Custom Storage Adapters

class CustomAdapter < PromptManager::Storage::Base
  # Add new optional methods for enhanced features

  def read_with_metadata(prompt_id)
    content = read(prompt_id)
    {
      content: content,
      metadata: get_metadata(prompt_id),
      last_modified: get_last_modified(prompt_id)
    }
  rescue PromptNotFoundError
    raise
  end

  def track_usage(prompt_id, render_time)
    # Optional: implement usage tracking
  end

  def get_versions(prompt_id)
    # Optional: implement versioning
    [{ version: 1, content: read(prompt_id), created_at: Time.current }]
  end
end

5. Update Application Code

# OLD (v0.9.x):
class EmailService
  def send_welcome_email(user)
    prompt = PromptManager::Prompt.new(id: 'welcome_email')
    content = prompt.render(user: user)
    # Send email
  end
end

# NEW (v1.0.0) - with error handling:
class EmailService
  def send_welcome_email(user)
    begin
      prompt = PromptManager::Prompt.new(id: 'welcome_email')
      content = prompt.render(user: user)
      EmailMailer.welcome(user, content).deliver_later
    rescue PromptManager::PromptNotFoundError
      Rails.logger.error "Welcome email template not found"
      EmailMailer.fallback_welcome(user).deliver_later
    rescue PromptManager::MissingParametersError => e
      Rails.logger.error "Missing email parameters: #{e.missing_parameters.join(', ')}"
      # Handle gracefully or re-raise
    end
  end
end

6. Migrate to Template Inheritance

Convert related prompts to use inheritance:

# Before: Separate files with duplicated structure
# welcome_email.txt, reset_password_email.txt, etc.

# After: Base template + specific content
# templates/email_base.txt
Subject: [SUBJECT]

Dear [USER_NAME],

[CONTENT]

Best regards,
The [COMPANY_NAME] Team

---
This email was sent to [USER_EMAIL].
To unsubscribe, visit [UNSUBSCRIBE_URL]

# welcome_email.txt  
//extends templates/email_base
//section subject: Welcome to [COMPANY_NAME]!
//section content: Thank you for joining us. We're excited to have you aboard!

# reset_password_email.txt
//extends templates/email_base
//section subject: Password Reset Request
//section content: Click here to reset your password: [RESET_URL]

Testing Migration

Automated Migration Test

# test/migration_test.rb
require 'test_helper'

class V100MigrationTest < ActiveSupport::TestCase
  def setup
    # Setup test data
  end

  test "basic prompt rendering still works" do
    prompt = PromptManager::Prompt.new(id: 'test_prompt')
    result = prompt.render(name: 'Test')
    assert_equal 'Hello Test!', result
  end

  test "template inheritance works" do
    prompt = PromptManager::Prompt.new(id: 'inherited_prompt')
    result = prompt.render(title: 'Test Page', content: 'Test content')
    assert_includes result, 'Test Page'
    assert_includes result, 'Test content'
  end

  test "custom directives work" do
    prompt = PromptManager::Prompt.new(id: 'directive_prompt')
    result = prompt.render
    assert_match /\d{4}-\d{2}-\d{2}/, result  # Should include timestamp
  end

  test "error handling provides detailed information" do
    prompt = PromptManager::Prompt.new(id: 'param_prompt')

    error = assert_raises(PromptManager::MissingParametersError) do
      prompt.render  # Missing required parameters
    end

    assert error.missing_parameters.any?
    assert error.suggestions.any?
  end
end

Performance Verification

# test/performance_test.rb
require 'test_helper'
require 'benchmark'

class V100PerformanceTest < ActiveSupport::TestCase
  test "rendering performance is acceptable" do
    prompt = PromptManager::Prompt.new(id: 'complex_prompt')
    params = { user: create_test_user, items: create_test_items(100) }

    time = Benchmark.measure do
      100.times { prompt.render(params) }
    end

    # Should render 100 complex prompts in under 1 second
    assert time.real < 1.0, "Rendering too slow: #{time.real}s"
  end
end

Rollback Procedures

Quick Rollback

# 1. Update Gemfile
gem 'prompt_manager', '~> 0.9.0'

# 2. Bundle install
bundle install

# 3. Rollback database
rails db:rollback STEP=1

# 4. Restart application

Configuration Rollback

Keep a backup of your v0.9.x configuration:

# config/initializers/prompt_manager_v09_backup.rb
PromptManager.configure do |config|
  config.storage = PromptManager::Storage::FileSystemAdapter.new(
    prompts_dir: Rails.root.join('app', 'prompts')
  )
  config.cache_prompts = true
  config.cache_ttl = 1800
end

Validation Checklist

  • Dependencies Updated: Gemfile shows v1.0.0
  • Database Migrated: New columns and indexes created
  • Configuration Updated: New configuration structure in use
  • Custom Adapters Updated: Enhanced methods implemented (if applicable)
  • Template Inheritance: Converted applicable prompts to use inheritance
  • Error Handling: Updated to use new error information
  • Tests Updated: All tests pass with new version
  • Performance Verified: Rendering performance is acceptable
  • Documentation Updated: Internal documentation reflects v1.0.0 features

Getting Support

  • Documentation: https://madbomber.github.io/prompt_manager
  • GitHub Issues: https://github.com/MadBomber/prompt_manager/issues
  • Community Forum: https://github.com/MadBomber/prompt_manager/discussions
  • Migration Support: Tag issues with migration-v1.0.0

Post-Migration Optimization

1. Enable New Features Gradually

Start with basic v1.0.0 functionality, then gradually enable: - Template inheritance - Custom directives - Performance monitoring - Advanced caching

2. Monitor Performance

Use the new performance monitoring to identify optimization opportunities:

# Check slow prompts
PromptManager.slow_prompts(threshold: 0.1).each do |prompt_data|
  puts "#{prompt_data[:id]}: #{prompt_data[:avg_time]}s average"
end

3. Optimize Storage

Consider upgrading storage adapters for better performance: - Use Redis adapter for high-frequency prompts - Enable database connection pooling - Implement read replicas for heavy read workloads


Congratulations! You've successfully migrated to PromptManager v1.0.0. This stable release provides a solid foundation for scaling your prompt management needs.