Upload
it-people
View
498
Download
1
Embed Size (px)
Citation preview
Мой соседЛегаси
Федоров Сергей, Evil Martians
Проект AmplifrПроект AmplifrПроект Amplifr
Проект Amplifr
— Музыкальная индустрия
— 4 соцсети
— Публикации
— Простая аналитика
Проект AmplifrПроект AmplifrПроект Amplifr
Проект Amplifr
— 100% SMM
— 12 соцсетей
— Публикации
— Аналитика
— Рекомендации
— Рассылки
Проект Amplifr
Эволюция VS Революция
О чем доклад?
О простых и дешевых приемах,которые позволят вам и вашейкоманде эффективно работатьс легаси кодом
ФорматированиеФорматированиеФорматирование
Форматирование
Форматирование
Никому не нравится читать плохоотформатированный код
Форматирование
def write_stats(publication, total, data = {})
activity = count_total_from_publication_data(publication)
update_activity(publication, activity)
total = activity.values.sum
if override_stat_date?(publication, 'created_at')
data[:date] = Time.parse(publication.data['created_at'])
end
unless dont_write_stats
if plays_total = publication.data.try(:[], 'playback_count').to_i
stat = \
Stat.record_total(
"soundcloud_plays",
{ project: publication.project, resource: publication }.merge(data),
plays_total
)
end
end
super
end
Форматирование
def write_stats(publication, total, data = {})
activity = count_total_from_publication_data(publication)
total = activity.values.sum
update_activity(publication, activity)
if override_stat_date?(publication, 'created_at')
data[:date] = Time.parse(publication.data['created_at'])
end
unless dont_write_stats
if plays_total = publication.data.try(:[], 'playback_count').to_i
stat = \
Stat.record_total(
"soundcloud_plays",
{ project: publication.project, resource: publication }.merge(data),
plays_total
)
end
end
super
end
Форматирование
def write_stats(publication, total, data = {})
activity = count_total_from_publication_data(publication)
total = activity.values.sum
update_activity(publication, activity)
if override_stat_date?(publication, 'created_at')
data[:date] = Time.parse(publication.data['created_at'])
end
unless dont_write_stats
if plays_total = publication.data.try(:[], 'playback_count').to_i
stat = \
Stat.record_total(
"soundcloud_plays",
{ project: publication.project, resource: publication }.merge(data),
plays_total
)
end
end
super
end
Форматирование
def write_stats(publication, total, data = {})
activity = count_total_from_publication_data(publication)
total = activity.values.sum
update_activity(publication, activity)
if override_stat_date?(publication, 'created_at')
data[:date] = Time.parse(publication.data['created_at'])
end
unless dont_write_stats
if plays_total = publication.data.try(:[], 'playback_count').to_i
stat = \
Stat.record_total(
"soundcloud_plays",
{ project: publication.project, resource: publication }.merge(data),
plays_total
)
end
end
super
end
Форматирование
def write_stats(publication, total, data = {})
activity = count_total_from_publication_data(publication)
total = activity.values.sum
update_activity(publication, activity)
if override_stat_date?(publication, 'created_at')
data[:date] = Time.parse(publication.data['created_at'])
end
unless dont_write_stats
if plays_total = publication.data.try(:[], 'playback_count').to_i
stat = \
Stat.record_total(
"soundcloud_plays",
{ project: publication.project, resource: publication }.merge(data),
plays_total
)
end
end
super
end
Форматирование
def write_stats(publication, total, data = {})
activity = count_total_from_publication_data(publication)
total = activity.values.sum
update_activity(publication, activity)
if override_stat_date?(publication, 'created_at')
data[:date] = Time.parse(publication.data['created_at'])
end
unless dont_write_stats
if plays_total = publication.data.try(:[], 'playback_count').to_i
stat = \
Stat.record_total(
"soundcloud_plays",
{ project: publication.project, resource: publication }.merge(data),
plays_total
)
end
end
super
end
Форматирование
def write_stats(publication, total, data = {})
activity = count_total_from_publication_data(publication)
total = activity.values.sum
update_activity(publication, activity)
if override_stat_date?(publication, 'created_at')
data[:date] = Time.parse(publication.data['created_at'])
end
unless dont_write_stats
if plays_total = publication.data.try(:[], 'playback_count').to_i
stat = \
Stat.record_total(
"soundcloud_plays",
{ project: publication.project, resource: publication }.merge(data),
plays_total
)
end
end
super
end
Форматирование
Современные IDE поддерживаютфункцию автоматическогоформатирования кода
Форматирование
Концепции, тесно связанные другс другом, должны находитьсяпоблизости друг от другапо вертикали
Форматирование
Взаимозависимые функциидолжны размещатьсяв нисходящем порядке
Форматирование
Единый стиль форматированияуменьшает вероятность ошибки
Источник: Robert C. Martin: Clean Code, A Handbook of Agile Software Craftsmanship
Неправильные именаНеправильные именаНеправильные имена
Неправильные имена
Неправильные имена
Самое трудноев программировании —это придумывать имена
Неправильные имена
Комментарии могут обманыватьтак же, как и неверные имена
Неправильные имена
Плохое имя — как магнитдля лишней функциональности
Неправильные имена
def access_token
# We store only refresh token and for each request
# just fetching new access token
api = Odnoklassniki::Client.new(
access_token: @account.token,
client_id: Settings.odnoklassniki.app_id,
client_secret: Settings.odnoklassniki.app_secret,
)
new_token = api.refresh_token!
raise Social::API::Unauthorized if new_token.blank?
api
end
Неправильные имена
def access_token
# We store only refresh token and for each request
# just fetching new access token
api = Odnoklassniki::Client.new(
access_token: @account.token,
client_id: Settings.odnoklassniki.app_id,
client_secret: Settings.odnoklassniki.app_secret,
)
new_token = api.refresh_token!
raise Social::API::Unauthorized if new_token.blank?
api
end
Неправильные имена
def access_token
# We store only refresh token and for each request
# just fetching new access token
api = Odnoklassniki::Client.new(
access_token: @account.token,
client_id: Settings.odnoklassniki.app_id,
client_secret: Settings.odnoklassniki.app_secret,
)
new_token = api.refresh_token!
if new_token.blank?
@account.disable!
raise Social::API::Unauthorized
end
api
end
Неправильные имена
def rest_client_with_refreshed_token
client = Odnoklassniki::Client.new(
access_token: @account.token,
client_id: Settings.odnoklassniki.app_id,
client_secret: Settings.odnoklassniki.app_secret,
)
refreshed_token = client.refresh_token!
raise Social::API::Unauthorized if refreshed_token.blank?
client
end
Дублирование и DRYДублирование и DRYДублирование и DRY
Дублирование и DRY
Дублирование и DRY
Все ли дублирование стоитустранять?
module Social
module API
class Vkontakte
def initialize(access_token)
@access_token = access_token
end
# ...
end
end
end
module Social
module API
class Twitter
def initialize(access_token)
@access_token = access_token
end
# ...
end
end
end
Дублирование и DRY
module Social
module API
class Base
def initialize(access_token)
@access_token = access_token
end
end
end
end
module Social
module API
class Vkontakte < Base
# ...
end
end
end
Дублирование и DRY
Дублирование и DRY
module Social
module API
class Base
def initialize(access_token)
@access_token = access_token
end
def rescue_api_errors(error, args)
# ..
end
def send_notification
# ...
end
end
end
end
Дублирование и DRY
Понятность кода не должна бытьценой устранения дублирования
Дублирование и DRY
С дублированием кода вполнеможно жить
Дублирование и DRY
Неверные абстракции устранятьсложнее, чем дублирование
Дублирование и DRY
Устраняя дублированиеруководствуйтесь примерамииспользования кода
ИнтерфейсыИнтерфейсыИнтерфейсы
Интерфейсы
вместо методоввместо методоввместо методов
вместо методов
Интерфейсы вместо методов
Лучший код — код которого нет
Интерфейсы вместо методов
Копирование или расширениесуществующих методов — ложныйreuse
Интерфейсы вместо методов
def fetch_account_publications(opts = {})
all = api_fetch
posts = all['feeds']
entities = all['entities'].try(:[], 'media_topics') || []
return unless posts.present? || entities.present?
posts.each do |story|
publication = find_publication(story)
likes = entities.find { |it| it['ref'] == story['target_refs'].try(:[], 0) }
likes = likes['like_summary']['count'] if likes
likes ||= 0
comments = story['discussion_summary'].try(:[], 'comments_count').to_i
shares = 0
update_activity(publication, {like: likes, share: shares, comment: comments})
total = comments + likes
write_stats(publication, total) if publication.is_a?(Social::ExternalPublication)
end
end
Интерфейсы вместо методов
def fetch_account_publications(opts = {})
all = api_fetch
posts = all['feeds']
entities = all['entities'].try(:[], 'media_topics') || []
return unless posts.present? || entities.present?
posts.each do |story|
publication = find_publication(story)
likes = entities.find { |it| it['ref'] == story['target_refs'].try(:[], 0) }
likes = likes['like_summary']['count'] if likes
likes ||= 0
comments = story['discussion_summary'].try(:[], 'comments_count').to_i
shares = 0
update_activity(publication, {like: likes, share: shares, comment: comments})
total = comments + likes
write_stats(publication, total) if publication.is_a?(Social::ExternalPublication)
end
end
Интерфейсы вместо методов
def fetch_account_publications(opts = {})
all = api_fetch
posts = all['feeds']
entities = all['entities'].try(:[], 'media_topics') || []
return unless posts.present? || entities.present?
posts.each do |story|
publication = find_publication(story)
likes = entities.find { |it| it['ref'] == story['target_refs'].try(:[], 0) }
likes = likes['like_summary']['count'] if likes
likes ||= 0
comments = story['discussion_summary'].try(:[], 'comments_count').to_i
shares = 0
update_activity(publication, {like: likes, share: shares, comment: comments})
total = comments + likes
write_stats(publication, total) if publication.is_a?(Social::ExternalPublication)
end
end
Интерфейсы вместо методов
def get_publication_activity(story)
publication = find_publication(story)
likes = entities.find { |it| it['ref'] == story['target_refs'].try(:[], 0) }
likes = likes['like_summary']['count'] if likes
likes ||= 0
comments = story['discussion_summary'].try(:[], 'comments_count').to_i
shares = 0
{likes: likes, comments: comments, shares: shares}
end
Интерфейсы вместо методов
class PublicationStatistics
def likes
# ...
end
def comments
# ...
end
def shares
# ...
end
end
Интерфейсы вместо методов
Производите анализ имеющегосякода
ТестыТестыТесты
Тесты
Тесты
Код без тестов — легаси код
Источник: Michael Feathers: Working Effectively with Legacy Code
Тесты
Как бы небыл понятен код —не факт, что ты ничего не сломал
Тесты
Критичные участки кода должныбыть покрыты тестами
РефакторингРефакторингРефакторинг
Рефакторинг
Рефакторинг
Довольно дорогой процессдля бизнеса
Рефакторинг
Очень сложно уложитьсяв отведенное время
Рефакторинг
Параллельная разработка делаетрефакторинг в разы сложнее
Заключение
Правило бойскаута — оставьместо стоянки чище, чем оно былодо твоего прихода
Заключение
Теория разбитых окон — устраняямалозначительные недочеты, выне допускаете более крупных
Заключение
Разработка через рефакторинг
ВопросыВопросыВопросы
Вопросы