Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AB infrastructure #357

Merged
merged 10 commits into from
Sep 6, 2018
Merged

AB infrastructure #357

merged 10 commits into from
Sep 6, 2018

Conversation

Ostrenkiy
Copy link
Contributor

Задача: #APPS-1964

Описание:
Инфраструктура для будущих a/b тестов.

  • Для каждого активного теста создаем объект класса, реализующего протокол ActiveABTest, в котором можно получить ID теста и значение для имени группы.
  • Класс ABTestService отвечает за определение группы пользователя и получение нужного значения.
  • RemoteConfig пришлось немного подкрутить.

@Ostrenkiy Ostrenkiy added this to the 1.67 milestone Sep 4, 2018
@Ostrenkiy Ostrenkiy self-assigned this Sep 4, 2018
@Ostrenkiy Ostrenkiy requested a review from kvld September 4, 2018 22:44
import UIKit

class ABSocialAuthString: ActiveABTest {
let ID = "ab_social_auth_string"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ID -> id (4.6 в стайлгайде)

import Foundation
import UIKit

class ABSocialAuthString: ActiveABTest {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Давай помечать класс как final, если не планируется от него наследоваться

associatedtype ValueType
var ID: String { get }
func value(group: String) -> ValueType?
var controlValue: ValueType { get }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Почему бы не добавить сюда еще var testValue: ValueType { get }? Тогда реализация value будет общая (просто switch по группе) и ее можно вынести в экстеншен к протоколу.

}

var value: T.ValueType {
if let resultGroup = RemoteConfig.shared.string(forKey: test.ID + "_result") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if let resultGroup = RemoteConfig.shared.string(forKey: test.ID + "_result"), 
   !resultGroup.isEmpty {
    defaults.set(resultGroup, forKey: test.ID)
    return test.value(group: resultGroup) ?? test.controlValue
}

Без лишней вложенности.

} else {
if RemoteConfig.shared.fetchComplete {
guard
let group = RemoteConfig.shared.string(forKey: test.ID),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Давай не будем изобретать новое форматирование длинных гвардов

@@ -14,7 +14,7 @@ class AnalyticsUserProperties {

static let shared = AnalyticsUserProperties()

private func setAmplitudeProperty(key: String, value: Any?) {
func setAmplitudeProperty(key: String, value: Any?) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Кмк, лучше сделать так же как и для других проперти:

func setABProperty(test: ABTest.IDType, value: ABTest.Group) { 
    self.setAmplitudeProperty(...)
}

#if DEBUG
activateDebugMode()
fetchDuration = 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Почему не поле класса и не в activateDebugMode?

@@ -110,4 +112,8 @@ class RemoteConfig {
let debugSettings = RemoteConfigSettings(developerModeEnabled: true)
FirebaseRemoteConfig.RemoteConfig.remoteConfig().configSettings = debugSettings
}

func string(forKey key: String) -> String? {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это конечно плохо, потому что тогда уж лучше переписать весь класс и сделать новый гибкий интерфейс. А получается, что какие-то ключи у нас обернуты в проперти, а какие-то мы дергаем без обертки 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Если мы так будем переписывать каждый класс, в котором что-то не нравится, не будем по пол года релизиться.

@@ -125,6 +127,10 @@ class SocialAuthPresenter {
}
}

var socialAuthHeaderString: String {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Проперти наверх

self.authAPI = authAPI
self.stepicsAPI = stepicsAPI
self.notificationStatusesAPI = notificationStatusesAPI
self.abSocialAuthService = abSocialAuthService
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Если у нас в модуле будет 10 A/B тестов, мы будем иметь 10 сервисов? Это, конечно, не очень и стоит подумать, как переделать так, чтобы было удобно.

Copy link
Contributor

@kvld kvld left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Некоторые из предыдущих замечаний тоже стоит исправить

@@ -38,7 +38,7 @@ class AnalyticsUserProperties {
}

func setUserID(to id: Int?) {
setAmplitudeProperty(key: "stepik_id", value: id)
setProperty(key: "stepik_id", value: id)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

У нас по-прежнему есть несколько провайдеров аналитики, поэтому подгонять под протокол меняя названия и модификатор доступа – не самая лучшая идея. Есть предложение сделать протокол не про задание проперти, а про задание проперти, связанной с A/B тестом. Тогда не нужно будет подгонять под протокол методы, а просто сделать отдельный метод, как я предлагал в предыдущем ревью.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не понимаю, чем плохо.
У нас есть протокол про проперти.
Я подогнал класс с проперти под этот протокол, просто переименовав один метод, который до этого был вообще приватным.
Другие провайдеры пока это не реализуют, но мы это и не используем.
В чем проблема здесь, я не вижу

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В чем смысл делать отдельный протокол про задание проперти для ab, если это будет то же самое, что и просто задание проперти?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Можно сказать про SOLID и ISP в нем, но к этому это вообще вряд ли относится, потому что разделить предлагается не по смыслу (не вижу кейсов, в котором задание юзер проперти и юзер проперти в ab-тесте будет различаться хоть как-то).

Stepic/AppDelegate.swift Outdated Show resolved Hide resolved
@@ -119,7 +119,7 @@ class SocialAuthViewController: UIViewController {

localize()

presenter = SocialAuthPresenter(authAPI: ApiDataDownloader.auth, stepicsAPI: ApiDataDownloader.stepics, notificationStatusesAPI: NotificationStatusesAPI(), view: self)
presenter = SocialAuthPresenter(authAPI: ApiDataDownloader.auth, stepicsAPI: ApiDataDownloader.stepics, notificationStatusesAPI: NotificationStatusesAPI(), splitTestingService: SplitTestingService(analyticsService: AnalyticsUserProperties.shared, storage: UserDefaults.standard), view: self)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Почему AnalyticsUserProperties вообще синглтон у нас?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Понятно, что так когда-то сделали. Но, может быстренько откажемся? К тому же, это не кажется сложным.
Внутри себя он все равно дергает синглтон из SDK аналитики – проблем с тем, что нужно иметь один инстанс нет.

@@ -554,6 +554,9 @@ PersonalDeadline = "Personal deadline";
DeadlineModeQuestion = "How much time would you like to spend studying on this course?";
PersonalDeadlineWidgetSuggestionText = "Constant learning is a key to success. We can smartly create personal learning schedule for you, so you'll never forget to study!";

/* Sign in AB */
SignInTitleSocialTest = "<b>Contunue</b> with";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continue

@Ostrenkiy Ostrenkiy merged commit b461d63 into dev Sep 6, 2018
@kvld kvld mentioned this pull request Sep 6, 2018
@kvld kvld deleted the feature/ab-infrastructure branch January 29, 2019 11:38
@ivan-magda ivan-magda added the a/b Pull requests that working with A/B testing label Jan 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a/b Pull requests that working with A/B testing
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants