Études de cas

Comment la Launch School Academy a multiplié par 3 l’engagement des élèves

Cet article a été écrit par Kevin Wang, instructeur en chef à la Tealeaf Academy (Launch School). La Tealeaf Academy est une école en ligne pour développeurs, et propose des bootcamps en ligne intensifs basés sur des projets autour du développement web. Si vous pensez que cet article est utile, vous devriez aller voir ce qu'ils font !

À la Tealeaf Academy, la création d’une expérience « Étudier ensemble, progresser ensemble » pour nos élèves est au cœur de notre méthode d’enseignement. L’un de nos outils principaux est le forum de discussion où les élèves posent des questions, partagent des idées, collaborent sur des devoirs, et où les professeurs interviennent rapidement pour aider les élèves à se débloquer sur des problèmes. L’une de nos récentes priorités était de réduire les frictions dans l’utilisation du forum de discussion et d’encourager davantage de discussions avec une notification par email complémentaire et un flux de travail « répondre à l’email pour publier sur le forum de discussion ».

Une fois que nous avons implémenté le code ci-dessous à l’aide de l’API Routes de Mailgun, l’activité sur notre forum de discussion a triplé, et les questions obtiennent désormais généralement une réponse en une heure, parfois même en quelques minutes, et les élèves sont capables de passer à la série de tâches suivante beaucoup plus rapidement. Voici comment nous avons procédé :

Notre flux de travail se présenterait comme suit :

  1. Lorsqu’une publication (très probablement, une question) est créée, tous les participants au cours sont informés par une notification par email
  2. Les participants au cours peuvent répondre aux notifications par email directement depuis leur boîte de réception d’emails, sans avoir à se connecter au cours
  3. Cette réponse sera publiée sur le forum de discussion en ligne et sera également envoyée aux autres participants au cours, afin de maintenir la conversation en cours.

Pourquoi nous choisissons Mailgun

L’élément clé de ce flux de travail est de recevoir et de parser les messages d’emails entrants. Nous avons examiné plusieurs services d’emailing, et avons finalement choisi Mailgun car :

  1. Il est très adapté aux développeurs – Nous sommes des développeurs, et Mailgun nous parle. Les API exposent de nombreuses options de bas niveau qui permettent des ajustements. Nous aimons les routes en particulier – C’est une belle couche d’abstraction qui rend l’intégration avec les applications très facile. (voyez comment nous l’utilisons ci-dessous)
  2. C’est le service le plus complet en fonctionnalités que nous ayons trouvé – nous pouvons utiliser Mailgun pour les emails transactionnels, les campagnes ainsi que les listes de contacts – c’est agréable de n’avoir qu’un seul fournisseur de services pour gérer tout ce dont nous avons besoin.
  3. Le prix est raisonnable et la possibilité de mettre à niveau vers une adresse IP dédiée et un DKIM personnalisé est appréciable, même si nous n’en avons pas encore besoin.
  4. Le support est de premier ordre. Il y a un chat en direct où je peux parler directement de mes problèmes à leurs développeurs et il nous a été très utile pour les résoudre.

Mise en place de l’infrastructure d’email

  • La première étape a été de créer un nouveau domaine sur Mailgun. Dans notre cas, il s’agit de messaging.gotealeaf.com que nous utilisons pour l’envoi et la réception d’emails.
  • Ensuite, nous avons créé un MailgunGateway comme wrapper pour l’API de Mailgun. Ce wrapper prend en charge toutes nos interactions avec Mailgun via notre compte. Ici, nous utilisons l’API send_batch_messages de Mailgun pour envoyer des emails HTML. Elle nous permet d’envoyer un message à plusieurs destinataires avec un appel d’API unique. Nous conservons notre clé API en tant que variables d’environnement sur le serveur pour plus de sécurité. Il existe également un filtre de livraison simple pour qu’aucun email ne soit envoyé dans l’environnement de développement ; Sur l’environnement de staging, tous les emails sont envoyés à Chris (mon co-fondateur) et à moi-même, ce qui nous permet de tester des choses sans craindre d’envoyer du spam à nos utilisateurs ; Ce n’est que sur l’environnement de production que les emails sont envoyés aux utilisateurs réels.
                            

                                class MailgunGateway  
  def send_batch_message(options={})
    RestClient.post(messaging_api_end_point,
        from: default_sender,
        to: delivery_filter(options[:to]),
        subject: options[:subject],
        html: options[:body],
        :"h:Reply-To" => options[:reply_to],
        :"recipient-variables" => options[:recipient_variables]
        ) if Rails.env.staging? || Rails.env.production?
    end
  end

  private

  def default_sender
    "Tealeaf Academy "
  end

  def api_key
    @api_key ||= ENV['mailgun_api_key']
  end

  def messaging_api_end_point
    @messaging_api_end_piont ||= 
        "https://api:#{api_key}@api.mailgun.net/v2/messaging.gotealeaf.com/messages"
  end

  def delivery_filter(emails)
    Rails.env.production? ? emails : "kevin@gotealeaf.com, chris@gotealeaf.com"
  end
end
                            
                        

Envoyer des notifications par email pour un nouveau post ou commentaire

Une fois qu’un post ou un commentaire est créé, nous envoyons une notification par email à tous les participants du cours. Nous envoyons des emails de manière synchrone pour le moment, mais à mesure que nous aurons plus d’utilisateurs, nous voudrons probablement déléguer cela à une tâche d’arrière-plan.

                            

                                Launch School Code 1class Courses::PostsController < AuthenticatedController  
  expose(:course)
  expose(:posts) { course.posts }
  expose(:post)

  def create
    post.user = current_user
    post.save
    CourseNotifier.new(course).notify_course_participants_on_new_discussion(post)
    redirect_to course_home_path(course)
  end

  ...
end

class Courses::Posts::CommentsController < AuthenticatedController  
  expose(:course)
  expose(:posts) { course.posts }
  expose(:post)
  expose(:comments) { post.comments }
  expose(:comment)

  def create
    comment.user = current_user
    comment.save
    CourseNotifier.new(course).notify_course_participants_on_new_discussion(comment)
    redirect_to course_home_path(course)
  end

  ...
end
                            
                        

CourseNotifier est la classe où nous plaçons notre logique spécifique à l’application concernant les notifications. Notez que MailgunGateway est injecté comme passerelle par défaut – cela date du temps où nous utilisions plusieurs services d’emailing pour les campagnes, les listes et les emails transactionnels. C’est moins nécessaire maintenant que nous avons consolidé tous les besoins de livraison d’emails vers Mailgun !

                            

                                class CourseNotifier

  attr_reader :course, :gateway

  def initialize(course, gateway=MailgunGateway.new)
    @course = course
    @gateway = gateway
  end

  def notify_course_participants_on_new_discussion(discussion)
    gateway.send_batch_message(
      to: notification_recipients(discussion).map(&:email).join(", "),
      subject: notification_subject(discussion),
      body: discussion_notification_text(discussion),
      reply_to: reply_to_address(discussion),
      recipient_variables: recipient_variables(
        notification_recipients(discussion)
      )
    )
  end

  ...

  private

  def notification_recipients(discussion)
    course.participants.reject {|participant| participant.email == discussion.user.email }
  end

  def notification_subject(discussion)
    discussion.is_a?(Post) ?
    "[Tealeaf Academy] #{discussion.user.name} Posted a New Message on the Discussion Board" :
    "[Tealeaf Academy] #{discussion.user.name} Replied to a Message on the Discussion Board"
  end

  def reply_to_address(discussion)
    "reply+#{discussion.token}@messaging.gotealeaf.com"
  end

  def recipient_variables(recipients)
    vars = recipients.map do |recipient|
      ""#{recipient.email}": {"name":"#{recipient.name}"}"
    end
    "{#{vars.join(', ')}}"
  end

  def discussion_notification_text(discussion)
<<-EMAIL


Hi %recipient.name%,

<p>#{discussion.user.name} says on the course dicussion board:</p>

"#{discussion.text}"
<br />
<p>Reply to this email directly or <a href="http://www.gotealeaf.com/courses/#{course.slug}/home">view it on the discussion board</a></p>

EMAIL  
  end
end
                            
                        

L’adresse replytoaddress est l’endroit où nous insérons le jeton du post dans l’en-tête « Reply-To ». Le contenu des emails est assez simple, nous le plaçons donc simplement ici dans la classe. Si nous avions un style d’email plus élaboré, nous aurions utilisé le rendu de modèle pour le gérer. Avec l’API sendbatchmessage de Mailgun, nous pouvons appeler l’API une seule fois pour envoyer à plusieurs destinataires, et la méthode recipient_variables est l’endroit où nous personnalisons les messages d’email pour chaque destinataire afin d’inclure leurs noms pour ajouter une touche personnelle.

Gestion des messages entrants dans l’application

En nous rendant sur Mailgun, sous « Routes » dans le panneau de configuration, nous avons créé la route suivante :

  • Expression de filtre : match_recipient(“reply+(.*)@messaging.gotealeaf.com”)
  • Action : forward(“http://www.gotealeaf.com/api/incoming_messages/?post_token=1”)

Lorsqu’un utilisateur répond à un email, il y répond à une adresse email telle que « reply+fj42gq4v@messaging.gotealeaf.com », et cette route transférera l’email à un webhook que nous exposons pour gérer les messages entrants.

                            

                                class Api::IncomingMessagesController < ApplicationController  
  skip_before_filter :verify_authenticity_token

  def create
    user = User.where(email: params['sender']).first
    post = Post.where(token: params['post_token']).first
    text = params["stripped-text"]
    if post && user && text.present?
      comment = post.comments.create(user: user, text: text)
      CourseNotifier.new(post.course).notify_course_participants_on_new_discussion(comment)
    end
    head(200)
  end
end
                            
                        

Ici, nous utilisons l’email de l’expéditeur pour trouver l’auteur, et utilisons le jeton du post pour trouver le post sous lequel cette réponse doit être regroupée. Mailgun nous donne le texte dépouillé très utile qui supprime la partie du message original pour ne contenir que la réponse réelle ! En fin de compte, nous renvoyons un en-tête 200 pour dire à Mailgun que cette interaction est réussie, sinon Mailgun pensera que notre serveur est en panne et continuera fidèlement d’essayer d’appeler notre webhook.

Le résultat de la mise en œuvre de ce flux de travail est impressionnant : l’activité sur notre forum de discussion a triplé, et les questions obtiennent désormais généralement une réponse dans l’heure, parfois même en quelques minutes, les étudiants peuvent passer à la série de tâches suivante beaucoup plus rapidement et nous sommes très satisfaits de la façon dont cela s’est passé.

« L’utilisation de Mailgun nous permet d’assurer un meilleur service à nos marketeurs. Ils comptent sur nous, et nous comptons sur Mailgun : et cette relation nous aide à maintenir notre crédibilité auprès de nos clients. »
Photo of Paulius Milisauskas
Paulius Milisauskas Vice-président des opérations clients
Tenez-moi au courant ! Recevez d’excellentes ressources dans votre boîte de réception chaque semaine.
Envoyez-moi la newsletter Mailjet. J’accepte expressément de recevoir la newsletter et je sais que je peux facilement me désabonner à tout moment.

Découvrez la newsletter de Mailjet chaque mois dans votre boîte de réception.